[ 
https://issues.apache.org/jira/browse/WAVE-169?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
 ]

Ali Lown updated WAVE-169:
--------------------------

    Summary: Implement private reply feature  (was: Implement private reply 
feature.  Starter project.)

> Implement private reply feature
> -------------------------------
>
>                 Key: WAVE-169
>                 URL: https://issues.apache.org/jira/browse/WAVE-169
>             Project: Wave
>          Issue Type: Bug
>            Priority: Minor
>              Labels: StarterProject, gsoc2014
>
> This is a starter project to add the private-reply feature from Google Wave.
> First, ensure that you are comfortable working in the Wave-In-A-Box 
> development environment (see http://www.waveprotocol.org/code), and consider 
> doing the sample tutorial.
> Most of the code to handle existing private replies is already present in 
> Wave-In-A-Box.  However, there is no UI action for creating them, so that 
> code is not currently exercised.  This project adds a logical UI action for 
> creating a private reply, in order to exercise that code path.  A separate 
> project will add the physical UI controls for handling the appropriate 
> gestures (drop-down menus etc.).
> 1) Piggy-back off the normal reply action.
> All the edit-related UI actions are implemented in 
> client/wavepanel/impl/edit/Actions.java.  First, hijack the existing reply 
> action, and change it to insert a whole conversation object (a private reply) 
> rather than just a regular reply thread.
> The conversation model interprets a wave as a collection of Conversations.  
> Some of these Conversations are anchored at blips of other Conversations.  
> There is no conversation-model API for creating private replies directly.  
> Instead, a private reply is formed by creating a new Conversation in the 
> collection, and then setting its anchor to point at the appropriate blip.  
> The collection of conversations in a wave is exposed through the type 
> model.conversation.ConversationView (the word 'view' is unfortunately 
> overloaded: in the model code, it is a legacy name that just means a 
> collection; in the client code, it means a UI object).
> Replace the reply code in Actions.reply(BlipView) with the private-reply 
> version:
> -    ConversationBlip reply =
> -        blip.appendInlineReplyThread(blip.getContent().size() - 
> 1).appendBlip()
> +    
> +    Conversation conv = wave.createConversation();
> +    ConversationBlip reply = conv.getRootThread().appendBlip();
> +    conv.setAnchor(blip.getConversation().createAnchor(blip));    
> This code adds a dependency on the conversation collection ('wave').
> 2) Inject the dependency.
> Undercurrent follows a manual dependency-injection (DI) style.  In short, 
> this just means that constructors are not public, and generally just set 
> fields to parameters; no work, and no method calls or calls to other 
> constructors.  Any logic required for construction is put in static factory 
> methods.  This makes it easy to instantiate objects with mocks for testing.
> Threading through the dependency on the whole conversation model requires the 
> following additions (Eclipse can do many of these for you automatically).
> * Add a 'private final ConversationView wave;' field to Actions;
> * Add it to the Actions constructor, following the DI pattern;
> * Add a 'ConverstionView wave' parameter to 
> EditActionsBuilder.createAndInstall
> * Update StageThree.DefaultProvider.createEditActions() with the local 
> variable:
>   
>   ...
>   ModelAsViewProvider views = stageTwo.getModelAsViewProvider();
> + ConversationView wave = stageTwo.getConversations();
>   BlipQueueRenderer blipQueue = stageTwo.getBlipQueue();
>   ...
> - Actions actions =
>     EditActionsBuilder.createAndInstall(panel, views, profiles, edit, 
> blipQueue, focus);
> + Actions actions =
>     EditActionsBuilder.createAndInstall(panel, views, wave, profiles, edit, 
> blipQueue, focus);
> This threading touches on two parts of Undercurrent: the layout of wave panel 
> features, and the staged loading process.  First, Wave panel features are 
> found in subpackages of wavepanel.impl (like edit, focus, reader, etc), and 
> each such subpackage has a Builder for creating and installing that feature 
> on demand. Second, related feature groups are bundled into 'stages' 
> (StageZero, StageOne, ...).  Editing features are bundled into StageThree.  
> Each stage has a provider class for creating and configuring each component 
> of that stage.  Applications can override parts of these providers in order 
> to customize or replace the various components in according to their specific 
> needs.  The StageThree.DefaultProvider.createEditActions() method provides 
> the default implementation and configuration of the wave-panel editing 
> actions.
> 3) Test the feature in the wave panel harness
> Start up the wave panel test harness, to try out this feature:
>  $ ant waveharness_hosted
> then visit the URL produced by the GWT code server (this is straight from the 
> tutorial).  Once the code has been loaded and the page is ready, click reply 
> on a blip.  Notice nothing happens in the UI, but there is an exception 
> reported at the bottom of the GWT code server log (in the tab that started up 
> for your browser session).  Unfortunately, there is not a clear indication 
> that something went wrong - you just have to get used to checking the GWT 
> code server tabs periodically for exceptions while testing.
> The exception is an NPE in LiveConversationViewRenderer.onConversationAdded().
> 4. Fix the renderer bug.
> In Undercurrent, live rendering is performed by renderers that observe 
> models.  The main renderer for a wave is the conversation renderer.  It has 
> event callbacks for conversation events, and these are invoked as the 
> conversation model(s) change.
> The code is:
>   public void onConversationAdded(ObservableConversation conversation) {
>     BlipView container = viewOf(conversation.getAnchor().getBlip());
>     if (container != null) {
>       ...
>     }
>     observe(conversation);
>   }
> The bug is that this code assumes that any conversations that show up 
> dynamically have anchors.  This is generally true, since the main way that 
> conversations show up dynamically is because of private-reply addition, and 
> private replies have anchors.  However, since the Wave platform has no 
> transaction facility, and most event APIs in Wave are synchronous, event 
> callbacks can often fire at inconvenient times (i.e., during the middle of a 
> compound action, revealing the model in a transient intermediate state).  
> Recall that the code to add the private reply created a new conversation 
> first, *then* set its anchor.  This causes this onConversationAdded event to 
> fire before the anchor is set.
> A simple null check fixes this.  Note that the LiveConversationViewRenderer 
> ensures that all conversations in view are being observed, and their events 
> are handled by a ConversationUpdater.  The anchor being set after the 
> conversation has been added is already handled by 
> ConversationUpdater.onAnchorChanged().
> - BlipView container = viewOf(conversation.getAnchor().getBlip());
> - if (container != null) {
> -   ConversationView conversationUi = 
> container.insertConversationBefore(null, conversation);
> - }
> + Anchor location = conversation.getAnchor();
> + if (location != null) {
> +   BlipView container = viewOf(location.getBlip());
> +   if (container != null) {
> +     ...
> +   }
> + }
>   observe(conversation);
> After saving this change, refresh the browser (you do not need to restart the 
> GWT code server), and try to reply to a blip again.
> 5. Fix the other renderer bug.
> Again, nothing happens in the UI, but there is an exception in the OOPHM log: 
> an ISE in LiveConversationViewRenderer$ConversationUpdater.onBlipAdded().  
> Again, this is a transient intermediate model state issue.  Since the new 
> private reply won't be rendered until it is anchored somewhere to the 
> conversation structure, there is no thread view for the new conversation's 
> root thread.  The onBlipAdded event is firing due to the code in 
> Actions.reply() adding a root blip to the new conversation, and it is trying 
> to render that new blip and attach the rendering to the rendering of its 
> containing thread.  Since it can not find that rendering (because it does not 
> exist yet), it throws an ISE.
> Since it is generally accepted that not all parts of the model are 
> necessarily rendered at all times, throwing an ISE in that situation does not 
> make sense.  Just delete the throw statement (and similarly in 
> onThreadAdded() in the same class.
> Save the changes and refresh the browser.  Observe that creating private 
> replies now works correctly in the harness.
> 6. Test concurrent behaviour in the full client.
> The feature is now working in the isolated environment of the wave harness.  
> The next step is to try it out in the full web client.  Build and run the 
> Wave In A Box server, then launch the GWT code server for the full client:
>  $ ant hosted_gwt
> In two browser windows, visit the URL:
>   http://<yourhostname>:9898/?gwt.codesvr=<yourhostname>:9997
> (the URL is not produced for you anymore, like in the harness, because of how 
> hostnames are set up for the Wave In a Box server).  If you get redirected 
> around because of needing to login, ensure that once you've returned to the 
> main page, that the ?gwt.codesvr=... URL parameter is put back.
> Create a wave in one window, and open it in the other window.  Then create a 
> private reply in one window, and observe it show up live in the other window.
> Congratulations!  You've added the code to create private replies.
> Now that you've verified that the code works, restore the original 
> implementation of Actions.reply(BlipView) to a regular reply, but add a 
> method Actions.replyPrivately(BlipView) to hold the private-reply creation 
> code.  Later, appropriate UI controls can be added to call that action.
> ---
> Issue imported from 
> http://code.google.com/p/wave-protocol/issues/detail?id=168
> Owner: [email protected]
> Label: Type-Defect
> Label: Priority-Medium
> Label: StarterProject
> Stars: 2
> State: open
> Status: Accepted



--
This message was sent by Atlassian JIRA
(v6.1.5#6160)

Reply via email to