[
https://issues.apache.org/jira/browse/WAVE-169?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
]
Ali Lown updated WAVE-169:
--------------------------
Labels: StarterProject gsoc2014 (was: )
> Implement private reply feature. Starter project.
> --------------------------------------------------
>
> 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)