org.eurekastreams.web.client.ui.common.widgets.activity.PostBoxComposite.java Source code

Java tutorial

Introduction

Here is the source code for org.eurekastreams.web.client.ui.common.widgets.activity.PostBoxComposite.java

Source

/*
 * Copyright (c) 2009-2013 Lockheed Martin Corporation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.eurekastreams.web.client.ui.common.widgets.activity;

import java.util.ArrayList;

import org.eurekastreams.server.action.request.stream.PostActivityRequest;
import org.eurekastreams.server.domain.DomainConversionUtility;
import org.eurekastreams.server.domain.EntityType;
import org.eurekastreams.server.domain.stream.ActivityDTO;
import org.eurekastreams.server.domain.stream.StreamScope;
import org.eurekastreams.server.domain.stream.StreamScope.ScopeType;
import org.eurekastreams.web.client.events.EventBus;
import org.eurekastreams.web.client.events.MessageAttachmentChangedEvent;
import org.eurekastreams.web.client.events.MessageStreamAppendEvent;
import org.eurekastreams.web.client.events.Observer;
import org.eurekastreams.web.client.events.data.GotAllPopularHashTagsResponseEvent;
import org.eurekastreams.web.client.events.data.GotSystemSettingsResponseEvent;
import org.eurekastreams.web.client.events.data.PostableStreamScopeChangeEvent;
import org.eurekastreams.web.client.model.ActivityModel;
import org.eurekastreams.web.client.model.AllPopularHashTagsModel;
import org.eurekastreams.web.client.model.SystemSettingsModel;
import org.eurekastreams.web.client.ui.Session;
import org.eurekastreams.web.client.ui.TimerFactory;
import org.eurekastreams.web.client.ui.TimerHandler;
import org.eurekastreams.web.client.ui.common.autocomplete.ExtendedTextArea;
import org.eurekastreams.server.domain.stream.LinkInformation;
import org.eurekastreams.web.client.ui.common.avatar.AvatarWidget.Size;
import org.eurekastreams.web.client.ui.common.stream.attach.Attachment;
import org.eurekastreams.web.client.ui.common.stream.attach.bookmark.AddLinkComposite;
import org.eurekastreams.web.client.ui.common.stream.attach.bookmark.Bookmark;
import org.eurekastreams.web.client.ui.common.stream.decorators.ActivityDTOPopulator;
import org.eurekastreams.web.client.ui.common.stream.decorators.ActivityDTOPopulatorStrategy;
import org.eurekastreams.web.client.ui.common.stream.decorators.object.NotePopulator;
import org.eurekastreams.web.client.ui.common.stream.decorators.verb.PostPopulator;
import org.eurekastreams.web.client.ui.common.stream.renderers.AvatarRenderer;

import com.google.gwt.core.client.GWT;
import com.google.gwt.dom.client.DivElement;
import com.google.gwt.dom.client.Document;
import com.google.gwt.dom.client.Style.Display;
import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.event.dom.client.BlurEvent;
import com.google.gwt.event.dom.client.BlurHandler;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.FocusEvent;
import com.google.gwt.event.dom.client.FocusHandler;
import com.google.gwt.event.dom.client.KeyCodes;
import com.google.gwt.event.dom.client.KeyDownEvent;
import com.google.gwt.event.dom.client.KeyDownHandler;
import com.google.gwt.event.dom.client.KeyUpEvent;
import com.google.gwt.event.dom.client.KeyUpHandler;
import com.google.gwt.event.dom.client.MouseOverEvent;
import com.google.gwt.event.dom.client.MouseOverHandler;
import com.google.gwt.resources.client.CssResource;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.HTMLPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.PushButton;
import com.google.gwt.user.client.ui.Widget;

/**
 * Post box.
 */
public class PostBoxComposite extends Composite {
    /** Max auto-complete hashtags to show. */
    private static final int MAX_HASH_TAG_AUTOCOMPLETE_ENTRIES = 10;

    /** Hide delay after blur on post box. */
    private static final Integer BLUR_DELAY = 500;

    /** Max chars for post. */
    private static final Integer POST_MAX = 250;

    /** Padding for hashtag dropdown. */
    private static final int HASH_TAG_DROP_DOWN_PADDING = 14;

    /**
     * Binder for building UI.
     */
    private static LocalUiBinder binder = GWT.create(LocalUiBinder.class);

    /**
     * 
     * Binder for building UI.
     */
    interface LocalUiBinder extends UiBinder<Widget, PostBoxComposite> {
    }

    /**
     * Post box CssResource style.
     */
    interface PostBoxStyle extends CssResource {
        /**
         * Visible post box style.
         * 
         * @return Visible post box style.
         */
        String visiblePostBox();

        /**
         * Post char count over limit.
         * 
         * @return Post char count over limit.
         */
        String postCharCountOverLimit();

        /**
         * Post button inactive.
         * 
         * @return post button inactive.
         */
        String postButtonInactive();

        /**
         * Active hashtag style.
         * 
         * @return Active hashtag style.
         */
        String activeHashTag();
    }

    /**
     * Post box CssResource style.
     */
    @UiField
    PostBoxStyle style;

    /**
     * UI element for poster avatar.
     */
    @UiField
    HTMLPanel posterAvatar;

    /**
     * UI element for post panel.
     */
    @UiField
    HTMLPanel postPanel;

    /**
     * UI element for post box.
     */
    @UiField
    ExtendedTextArea postBox;

    /**
     * UI element for post options.
     */
    @UiField
    DivElement postOptions;

    /**
     * UI element for post button.
     */
    @UiField
    PushButton postButton;

    /**
     * UI element for post char count.
     */
    @UiField
    DivElement postCharCount;

    /**
     * UI element for add link composite.
     */
    @UiField
    AddLinkComposite addLinkComposite;

    /**
     * Hash Tags.
     */
    @UiField
    FlowPanel hashTags;

    /**
     * The content warning.
     */
    @UiField
    FlowPanel contentWarning;

    /**
     * The content warning container.
     */
    @UiField
    DivElement contentWarningContainer;

    /**
     * Currently active item.
     */
    private Integer activeItemIndex = null;

    /**
     * Timer factory.
     */
    private final TimerFactory timerFactory = new TimerFactory();

    /**
     * Current stream to post to.
     */
    private StreamScope currentStream = new StreamScope(ScopeType.PERSON,
            Session.getInstance().getCurrentPerson().getAccountId());

    /**
     * Avatar Renderer.
     */
    private final AvatarRenderer avatarRenderer = new AvatarRenderer();

    /**
     * Activity Populator.
     */
    private final ActivityDTOPopulator activityPopulator = new ActivityDTOPopulator();

    /**
     * Attachment.
     */
    private Attachment attachment = null;

    /**
     * All hash tags.
     */
    private ArrayList<String> allHashTags;

    /**
     * Default constructor.
     */
    public PostBoxComposite() {
        initWidget(binder.createAndBindUi(this));
        buildPage();
    }

    /**
     * Build page.
     */
    private void buildPage() {
        posterAvatar.add(avatarRenderer.render(Session.getInstance().getCurrentPerson().getEntityId(),
                Session.getInstance().getCurrentPerson().getAvatarId(), EntityType.PERSON, Size.Small));
        postCharCount.setInnerText(POST_MAX.toString());
        checkPostBox();
        postBox.setLabel("Post to your stream...");
        postBox.reset();

        addEvents();

        postBox.addKeyUpHandler(new KeyUpHandler() {
            public void onKeyUp(final KeyUpEvent event) {
                checkPostBox();
            }
        });

        postBox.addFocusHandler(new FocusHandler() {
            public void onFocus(final FocusEvent event) {
                postOptions.addClassName(style.visiblePostBox());
            }
        });

        postBox.addBlurHandler(new BlurHandler() {
            public void onBlur(final BlurEvent event) {

                timerFactory.runTimer(BLUR_DELAY, new TimerHandler() {
                    public void run() {
                        if (postBox.getText().trim().length() == 0 && !addLinkComposite.inAddMode()
                                && attachment == null) {
                            postOptions.removeClassName(style.visiblePostBox());
                            postBox.getElement().getStyle().clearHeight();
                            postBox.reset();
                        }
                    }
                });
            }
        });

        setupPostButtonClickHandler();

        postBox.addKeyDownHandler(new KeyDownHandler() {
            public void onKeyDown(final KeyDownEvent event) {
                if ((event.getNativeKeyCode() == KeyCodes.KEY_TAB || event.getNativeKeyCode() == KeyCodes.KEY_ENTER)
                        && !event.isAnyModifierKeyDown() && activeItemIndex != null) {
                    hashTags.getWidget(activeItemIndex).getElement().dispatchEvent(
                            Document.get().createClickEvent(1, 0, 0, 0, 0, false, false, false, false));
                    event.preventDefault();
                    event.stopPropagation();
                    // clearSearch();

                } else if (event.getNativeKeyCode() == KeyCodes.KEY_DOWN && activeItemIndex != null) {
                    if (activeItemIndex + 1 < hashTags.getWidgetCount()) {
                        selectItem((Label) hashTags.getWidget(activeItemIndex + 1));
                    }
                    event.preventDefault();
                    event.stopPropagation();
                } else if (event.getNativeKeyCode() == KeyCodes.KEY_UP && activeItemIndex != null) {
                    if (activeItemIndex - 1 >= 0) {
                        selectItem((Label) hashTags.getWidget(activeItemIndex - 1));
                    }
                    event.preventDefault();
                    event.stopPropagation();
                } else if (event.getNativeKeyCode() == KeyCodes.KEY_ENTER && event.isControlKeyDown()) {
                    postButton.getElement().dispatchEvent(
                            Document.get().createClickEvent(1, 0, 0, 0, 0, false, false, false, false));
                    event.preventDefault();
                    event.stopPropagation();
                }

            }
        });

        postBox.addKeyUpHandler(new KeyUpHandler() {
            public void onKeyUp(final KeyUpEvent event) {
                int tagsAdded = 0;

                String postText = postBox.getText();
                if (!postText.endsWith(" ")) {
                    String[] words = postText.split("\\s");
                    if (words.length >= 1) {
                        final String lastWord = words[words.length - 1];
                        if (lastWord.startsWith("#")) {
                            boolean activeItemSet = false;
                            Label firstItem = null;

                            for (String hashTag : allHashTags) {
                                if (hashTag.startsWith(lastWord)) {
                                    // get list ready on first tag added
                                    if (tagsAdded == 0) {
                                        hashTags.clear();

                                        String boxHeight = postBox.getElement().getStyle().getHeight().replace("px",
                                                "");
                                        if (boxHeight.isEmpty()) {
                                            boxHeight = "44";
                                        }
                                        hashTags.getElement().getStyle().setTop(
                                                Integer.parseInt(boxHeight) + HASH_TAG_DROP_DOWN_PADDING, Unit.PX);
                                        hashTags.setVisible(true);
                                    }

                                    final String hashTagFinal = hashTag;
                                    final Label tagLbl = new Label(hashTagFinal);
                                    tagLbl.addClickHandler(new ClickHandler() {
                                        public void onClick(final ClickEvent event) {
                                            String postText = postBox.getText();
                                            postText = postText.substring(0, postText.length() - lastWord.length())
                                                    + hashTagFinal + " ";
                                            postBox.setText(postText);
                                            hashTags.setVisible(false);
                                            hashTags.clear();
                                            activeItemIndex = null;
                                        }
                                    });
                                    tagLbl.addMouseOverHandler(new MouseOverHandler() {
                                        public void onMouseOver(final MouseOverEvent arg0) {
                                            selectItem(tagLbl);
                                        }
                                    });
                                    hashTags.add(tagLbl);

                                    if (firstItem == null) {
                                        firstItem = tagLbl;
                                    }

                                    if (activeItemIndex != null
                                            && activeItemIndex.equals(hashTags.getWidgetCount() - 1)) {
                                        activeItemIndex = null;
                                        activeItemSet = true;
                                        selectItem(tagLbl);
                                    }

                                    tagsAdded++;
                                    if (tagsAdded >= MAX_HASH_TAG_AUTOCOMPLETE_ENTRIES) {
                                        break;
                                    }
                                }
                            }

                            if (!activeItemSet && firstItem != null) {
                                activeItemIndex = null;
                                selectItem(firstItem);
                            }
                        }
                    }
                }

                if (tagsAdded == 0) {
                    hashTags.setVisible(false);
                    activeItemIndex = null;
                }
            }
        });

        AllPopularHashTagsModel.getInstance().fetch(null, true);
        SystemSettingsModel.getInstance().fetch(null, true);

        hashTags.setVisible(false);
    }

    /**
     * Add events.
     */
    private void addEvents() {
        EventBus.getInstance().addObserver(MessageStreamAppendEvent.class,
                new Observer<MessageStreamAppendEvent>() {
                    public void update(final MessageStreamAppendEvent event) {
                        attachment = null;
                        addLinkComposite.close();
                        postBox.setText("");
                        postBox.reset();
                        postBox.getElement().getStyle().clearHeight();
                        postOptions.removeClassName(style.visiblePostBox());
                        checkPostBox();
                    }
                });

        EventBus.getInstance().addObserver(PostableStreamScopeChangeEvent.class,
                new Observer<PostableStreamScopeChangeEvent>() {
                    public void update(final PostableStreamScopeChangeEvent stream) {
                        currentStream = stream.getResponse();
                        if (currentStream != null && !"".equals(currentStream.getDisplayName())) {
                            if (currentStream.getScopeType().equals(ScopeType.PERSON)) {
                                if (currentStream.getDisplayName().endsWith("s")) {
                                    postBox.setLabel("Post to " + currentStream.getDisplayName() + "' stream...");
                                } else {
                                    postBox.setLabel("Post to " + currentStream.getDisplayName() + "'s stream...");
                                }
                            } else {
                                postBox.setLabel("Post to the " + currentStream.getDisplayName() + " stream...");
                            }
                        } else {
                            postBox.setLabel("Post to your stream...");
                        }

                        postPanel.setVisible(stream.getResponse().getScopeType() != null);
                    }
                });

        EventBus.getInstance().addObserver(MessageAttachmentChangedEvent.class,
                new Observer<MessageAttachmentChangedEvent>() {
                    public void update(final MessageAttachmentChangedEvent evt) {
                        attachment = evt.getAttachment();
                        // When adding an attachment,
                        // make the "Post" Button visible.
                        checkPostBox();
                    }
                });

        EventBus.getInstance().addObserver(GotSystemSettingsResponseEvent.class,
                new Observer<GotSystemSettingsResponseEvent>() {
                    public void update(final GotSystemSettingsResponseEvent event) {
                        String warning = event.getResponse().getContentWarningText();
                        if (warning != null && !warning.isEmpty()) {
                            contentWarning.getElement().setInnerHTML(warning);
                        } else {
                            contentWarning.setVisible(false);
                            contentWarningContainer.getStyle().setDisplay(Display.NONE);
                        }
                    }
                });

        Session.getInstance().getEventBus().addObserver(GotAllPopularHashTagsResponseEvent.class,
                new Observer<GotAllPopularHashTagsResponseEvent>() {
                    public void update(final GotAllPopularHashTagsResponseEvent event) {
                        allHashTags = new ArrayList<String>(event.getResponse());
                    }
                });

    }

    /**
     * Check the post box.
     */
    private void checkPostBox() {
        postCharCount.setInnerText(Integer.toString(POST_MAX - postBox.getText().length()));

        if (POST_MAX - postBox.getText().length() < 0) {
            postCharCount.addClassName(style.postCharCountOverLimit());
        } else {
            postCharCount.removeClassName(style.postCharCountOverLimit());
        }

        if ((postBox.getText().length() > 0 && POST_MAX - postBox.getText().length() >= 0) || attachment != null) {
            postButton.removeStyleName(style.postButtonInactive());
        } else {
            postButton.addStyleName(style.postButtonInactive());
        }
    }

    /**
     * Select an item.
     * 
     * @param item
     *            the item.
     */
    private void selectItem(final Label item) {
        if (activeItemIndex != null) {
            hashTags.getWidget(activeItemIndex).removeStyleName(style.activeHashTag());
        }
        item.addStyleName(style.activeHashTag());
        activeItemIndex = hashTags.getWidgetIndex(item);
    }

    /**
     * Setup the post button click handler - to prevent the checkstyle error of too-long method name.
     */
    private void setupPostButtonClickHandler() {
        postButton.addClickHandler(new ClickHandler() {
            /**
             * Handle the button click.
             * 
             * @param inEvent
             *            the click event
             */
            public void onClick(final ClickEvent inEvent) {
                if (!postButton.getStyleName().contains(style.postButtonInactive())) {
                    if (!isLinkAttached() && addLinkComposite.inAddMode() && doesUnattachedLinkTextHaveText()) {
                        // Assume that the user meant to attach the link,
                        // so attach the link to the message.

                        // Grab the link text from the unattached link textbox
                        LinkInformation linkInformation = new LinkInformation();
                        String linkText = addLinkComposite.getLinkText();
                        linkInformation.setUrl(linkText);
                        linkInformation.setTitle(linkText);

                        Bookmark bookmark = new Bookmark(linkInformation);
                        attachment = bookmark;
                    }

                    ActivityDTOPopulatorStrategy objectStrat = attachment != null ? attachment.getPopulator()
                            : new NotePopulator();

                    ActivityDTO activity = activityPopulator.getActivityDTO(postBox.getText(),
                            DomainConversionUtility.convertToEntityType(currentStream.getScopeType()),
                            currentStream.getUniqueKey(), new PostPopulator(), objectStrat);

                    PostActivityRequest postRequest = new PostActivityRequest(activity);

                    ActivityModel.getInstance().insert(postRequest);

                    postButton.addStyleName(style.postButtonInactive());
                }
            }
        });
    }

    /**
     * Checks whether there's text in an unattached link.
     * 
     * @return boolean true or false
     */
    private boolean doesUnattachedLinkTextHaveText() {
        String linkText = addLinkComposite.getLinkText();
        String basicLegitimateHttpPattern = "://";

        // Does the unattached link have "http://" and a period
        if (linkText.toLowerCase().contains(basicLegitimateHttpPattern) && linkText.contains(".")) {
            return true;
        }
        return false;
    }

    /**
     * Returns whether a link is attached to a user's unsubmitted post.
     * 
     * @return boolean true or false
     */
    private boolean isLinkAttached() {
        return addLinkComposite.hasAttachment();
    }

}