Package org.eurekastreams.web.client.ui.pages.activity

Source Code of org.eurekastreams.web.client.ui.pages.activity.ActivityContent

/*
* Copyright (c) 2011-2012 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.pages.activity;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.eurekastreams.server.domain.AvatarUrlGenerator;
import org.eurekastreams.server.domain.EntityType;
import org.eurekastreams.server.domain.Follower.FollowerStatus;
import org.eurekastreams.server.domain.Identifiable;
import org.eurekastreams.server.domain.Page;
import org.eurekastreams.server.domain.PagedSet;
import org.eurekastreams.server.domain.stream.ActivityDTO;
import org.eurekastreams.server.domain.stream.Stream;
import org.eurekastreams.server.domain.stream.StreamFilter;
import org.eurekastreams.server.domain.stream.StreamScope;
import org.eurekastreams.server.domain.stream.StreamScope.ScopeType;
import org.eurekastreams.server.search.modelview.DomainGroupModelView;
import org.eurekastreams.server.search.modelview.PersonModelView;
import org.eurekastreams.server.search.modelview.PersonModelView.Role;
import org.eurekastreams.web.client.events.CustomStreamCreatedEvent;
import org.eurekastreams.web.client.events.CustomStreamDeletedEvent;
import org.eurekastreams.web.client.events.CustomStreamUpdatedEvent;
import org.eurekastreams.web.client.events.EventBus;
import org.eurekastreams.web.client.events.HistoryViewsChangedEvent;
import org.eurekastreams.web.client.events.MessageStreamAppendEvent;
import org.eurekastreams.web.client.events.Observer;
import org.eurekastreams.web.client.events.ShowNotificationEvent;
import org.eurekastreams.web.client.events.StreamReinitializeRequestEvent;
import org.eurekastreams.web.client.events.StreamSearchBeginEvent;
import org.eurekastreams.web.client.events.UpdateHistoryEvent;
import org.eurekastreams.web.client.events.UpdatedHistoryParametersEvent;
import org.eurekastreams.web.client.events.data.AddedFeaturedStreamResponseEvent;
import org.eurekastreams.web.client.events.data.GotActivityResponseEvent;
import org.eurekastreams.web.client.events.data.GotCurrentUserCustomStreamsResponseEvent;
import org.eurekastreams.web.client.events.data.GotCurrentUserStreamBookmarks;
import org.eurekastreams.web.client.events.data.GotGroupModelViewInformationResponseEvent;
import org.eurekastreams.web.client.events.data.GotPersonFollowerStatusResponseEvent;
import org.eurekastreams.web.client.events.data.GotPersonalInformationResponseEvent;
import org.eurekastreams.web.client.events.data.GotStreamActivitySubscriptionResponseEvent;
import org.eurekastreams.web.client.events.data.GotStreamResponseEvent;
import org.eurekastreams.web.client.events.data.InsertedGroupMemberResponseEvent;
import org.eurekastreams.web.client.events.data.InsertedPersonFollowerResponseEvent;
import org.eurekastreams.web.client.events.data.InsertedRequestForGroupMembershipResponseEvent;
import org.eurekastreams.web.client.events.data.PostableStreamScopeChangeEvent;
import org.eurekastreams.web.client.events.data.StreamActivitySubscriptionChangedEvent;
import org.eurekastreams.web.client.events.data.UpdatedGroupStickyActivityEvent;
import org.eurekastreams.web.client.history.CreateUrlRequest;
import org.eurekastreams.web.client.jsni.EffectsFacade;
import org.eurekastreams.web.client.jsni.WidgetJSNIFacadeImpl;
import org.eurekastreams.web.client.model.ActivityModel;
import org.eurekastreams.web.client.model.CustomStreamModel;
import org.eurekastreams.web.client.model.Deletable;
import org.eurekastreams.web.client.model.GadgetModel;
import org.eurekastreams.web.client.model.GroupActivitySubscriptionModel;
import org.eurekastreams.web.client.model.GroupMembershipRequestModel;
import org.eurekastreams.web.client.model.GroupModel;
import org.eurekastreams.web.client.model.PersonActivitySubscriptionModel;
import org.eurekastreams.web.client.model.PersonalInformationModel;
import org.eurekastreams.web.client.model.StreamBookmarksModel;
import org.eurekastreams.web.client.model.StreamModel;
import org.eurekastreams.web.client.model.requests.AddGadgetToStartPageRequest;
import org.eurekastreams.web.client.ui.Session;
import org.eurekastreams.web.client.ui.common.SpinnerLabelButton;
import org.eurekastreams.web.client.ui.common.avatar.AvatarBadgeManager;
import org.eurekastreams.web.client.ui.common.avatar.AvatarLinkPanel;
import org.eurekastreams.web.client.ui.common.avatar.AvatarWidget.Size;
import org.eurekastreams.web.client.ui.common.dialog.Dialog;
import org.eurekastreams.web.client.ui.common.notifier.Notification;
import org.eurekastreams.web.client.ui.common.stream.ActivityDetailPanel;
import org.eurekastreams.web.client.ui.common.stream.GroupEmailSubscribeOptionsDialogContent;
import org.eurekastreams.web.client.ui.common.stream.StreamJsonRequestFactory;
import org.eurekastreams.web.client.ui.common.stream.StreamSearchStatusWidget;
import org.eurekastreams.web.client.ui.common.stream.StreamToUrlTransformer;
import org.eurekastreams.web.client.ui.common.stream.UnseenActivityNotificationPanel;
import org.eurekastreams.web.client.ui.common.stream.filters.list.CustomStreamDialogContent;
import org.eurekastreams.web.client.ui.common.stream.renderers.ShowRecipient;
import org.eurekastreams.web.client.ui.common.stream.renderers.StickyActivityRenderer;
import org.eurekastreams.web.client.ui.common.stream.renderers.StreamMessageItemRenderer;
import org.eurekastreams.web.client.ui.common.widgets.activity.PostBoxComposite;
import org.eurekastreams.web.client.ui.common.widgets.activity.StreamDetailsComposite;
import org.eurekastreams.web.client.ui.common.widgets.activity.StreamDetailsComposite.CustomAvatar;
import org.eurekastreams.web.client.ui.pages.master.StaticResourceBundle;

import com.google.gwt.core.client.GWT;
import com.google.gwt.core.client.Scheduler;
import com.google.gwt.core.client.Scheduler.ScheduledCommand;
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.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.KeyCodes;
import com.google.gwt.event.dom.client.KeyUpEvent;
import com.google.gwt.event.dom.client.KeyUpHandler;
import com.google.gwt.json.client.JSONArray;
import com.google.gwt.json.client.JSONObject;
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.History;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.Window;
//import com.google.gwt.user.client.ui.Anchor;
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.Hyperlink;
import com.google.gwt.user.client.ui.Image;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.Panel;
import com.google.gwt.user.client.ui.SimplePanel;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.UIObject;
import com.google.gwt.user.client.ui.Widget;
import com.google.gwt.http.client.URL;

/**
* Activity Page.
*/
public class ActivityContent extends Composite
{
    /** Text displayed for locked users. */
    private static final String LOCKED_USER_TEXT = "This employee has no profile in Eureka Streams.  This could be due"
            + " to an incorrect or outdated link, a change in assignment within the company, or leaving the company.";

    /** Amount of time to wait after a key is pressed before performing a search. */
    private static final int SEARCH_UPDATE_DELAY = 500;

    /** Amount of time to wait after a key is pressed to update the URL with the search term. */
    private static final int SEARCH_URL_UPDATE_DELAY = 2000;

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

    /**
     * CSS resource.
     */
    interface ActivityStyle extends CssResource
    {
        /**
         * Active sort style.
         *
         * @return Active sort style
         */
        String activeSort();

        /**
         * Active stream style.
         *
         * @return Active stream style.
         */
        String activeStream();

        /**
         * Stream options child.
         *
         * @return Stream options child.
         */
        String streamOptionChild();

        /**
         * Delete bookmark.
         *
         * @return delete bookmark.
         */
        String deleteBookmark();

        /**
         * Edit custom stream.
         *
         * @return edit custom stream.
         */
        String editCustomStream();

        /**
         * The stream name style.
         *
         * @return the stream name style.
         */
        String streamName();

        /**
         * Active search style.
         *
         * @return active search style.
         */
        String activeSearch();

        /**
         * Current user link style.
         *
         * @return current user stream style.
         */
        String currentUserStreamLink();

        /**
         * Small avatar.
         *
         * @return small avatar style.
         */
        String smallAvatar();

        /**
         * Current user configure link.
         *
         * @return current user configure link.
         */
        String currentUserConfLink();

        /**
         * Message when no bookmarks exist.
         *
         * @return message when no bookmarks exist.
         */
        String noBookmarksMessage();

        /** @return The stream container panel. */
        @ClassName("stream-container-panel")
        String streamContainerPanel();
    }

    /**
     * CSS style.
     */
    @UiField
    ActivityStyle style;

    /**
     * Stream details.
     */
    @UiField
    StreamDetailsComposite streamDetailsComposite;

    /**
     * No results panel.
     */
    @UiField
    DivElement noResults;

    /**
     * Search container.
     */
    @UiField
    DivElement searchContainer;

    /**
     * UI element for streams.
     */
    @UiField
    FlowPanel streamPanel;

    /**
     * UI element for bookmarks.
     */
    @UiField
    FlowPanel bookmarkList;

    /**
     * UI element for stream container.
     */
    @UiField
    HTMLPanel streamContainerPanel;

    /**
     * UI element for filters.
     */
    @UiField
    FlowPanel filterList;

    /**
     * UI element for default streams.
     */
    @UiField
    FlowPanel defaultList;

    /**
     * UI element for default streams.
     */
    @UiField
    FlowPanel errorPanel;

    /**
     * UI element for recent sort.
     */
    @UiField
    Hyperlink recentSort;

    /**
     * UI element for popular sort.
     */
    @UiField
    Hyperlink popularSort;

    /**
     * UI element for active sort.
     */
    @UiField
    Hyperlink activeSort;

    /**
     * UI element for activity loading spinner.
     */
    @UiField
    DivElement activitySpinner;

    /**
     * Feed link.
     */
    @UiField
    Label feedLink;

    /**
     * UI element for more spinner.
     */
    @UiField
    DivElement moreSpinner;

    /**
     * UI element for more link.
     */
    @UiField
    Label moreLink;

    /**
     * UI element for adding a bookmark.
     */
    @UiField
    Label addBookmark;

    /**
     * UI element for adding a a stream to the start page.
     */
    @UiField
    Label addToStartPage;

    /**
     * Create Filter.
     */
    @UiField
    Label createFilter;

    /**
     * Subscribe via email.
     */
    @UiField
    Label subscribeViaEmail;

    /**
     * Stream search status widget.
     */
    @UiField
    StreamSearchStatusWidget streamSearchStatusWidget;

    /** Link to get contact for emailing to stream. */
    //@UiField
    //Anchor getEmailContactLink;

    /**
     * Panel for unseen activity notifications - hidden when viewing a single activity.
     */
    @UiField
    UnseenActivityNotificationPanel unseenActivityNotificationPanel;

    /**
     * Message Renderer.
     */
    StreamMessageItemRenderer renderer = new StreamMessageItemRenderer(ShowRecipient.YES);

    /** Sticky activity renderer (for groups only). */
    private static StickyActivityRenderer stickyActivityRenderer = new StickyActivityRenderer();

    /**
     * Newest activity ID.
     */
    private long longNewestActivityId = 0L;

    /**
     * Oldest Activity ID.
     */
    private long longOldestActivityId = 0;

    /**
     * Current Request.
     */
    private JSONObject currentRequestObj = null;

    /**
     * Search Box.
     */
    @UiField
    TextBox searchBox;

    /**
     * Stream options panel.
     */
    @UiField
    DivElement streamOptionsPanel;

    /**
     * Current stream scope.
     */
    private StreamScope currentStream;

    /**
     * Current scope id.
     */
    private long currentScopeId;

    /**
     * Current stream display name.
     */
    private String currentDisplayName;

    /**
     * Entity for the current stream. Used by the sticky activity logic to determine if any stick/unstick events pertain
     * to the current view. Would use currentStream, but there is other code that clobbers the entity type if the stream
     * isn't postable and thus renders currentStream useless for the purpose.
     */
    private Identifiable currentStreamEntity;

    /**
     * New activity polling.
     */
    private static final int NEW_ACTIVITY_POLLING_DELAY = 1200000;

    /**
     * Custom streams map.
     */
    private final HashMap<Long, StreamNamePanel> customStreamWidgetMap = new HashMap<Long, StreamNamePanel>();

    /**
     * Stream bookmarks map.
     */
    private final HashMap<String, StreamNamePanel> bookmarksWidgetMap = new HashMap<String, StreamNamePanel>();

    /**
     * Currently active stream.
     */
    private Panel currentlyActiveStream = null;

    /**
     * Following filter panel.
     */
    private Panel followingFilterPanel = null;

    /**
     * Everyone filter panel.
     */
    private Panel everyoneFilterPanel = null;

    /**
     * Is subscribed.
     */
    private boolean isSubscribed = false;

    /**
     * Post Box.
     */
    @UiField
    PostBoxComposite postBox;

    /**
     * User panel.
     */
    @UiField
    FlowPanel userPanel;

    /**
     * Current sort keyword.
     */
    private String sortKeyword = "";

    /**
     * Single activity mode.
     */
    private boolean singleActivityMode;

    /**
     * Bookmarks initially loaded.
     */
    private boolean bookmarksLoaded = false;

    /**
     * Bookmarks initially loaded.
     */
    private boolean customStreamsLoaded = false;

    /**
     * If the page has ran init.
     */
    private boolean hasInited = false;

    /** Stream to URL transformer. */
    private static final StreamToUrlTransformer STREAM_URL_TRANSFORMER = new StreamToUrlTransformer();

    /**
     * If the entity still needs to be received before making the activity query.
     *
     * Explanation: Fetching the activities for a group requires knowing which activity is sticky so it can be excluded
     * on the query. So the query must be built in loadStream and the DomainGroupModelView must be recieved.
     * Unfortunately, the event bus notifies inline instead of queuing, thus the call to the GroupModel in loadStream
     * may fire its event immediately (if the group is in cache), causing the group to be received before loadStream
     * finishes. So we have to handle two different possible orders. The approach taken is to set this flag and the one
     * below; whenever they are both reset, the activity query is sent.
     */
    private boolean deferLoadAwaitingEntityReceived;

    /**
     * If the activity query isn't done being constructed yet (before making the activity query). See
     * deferLoadAwaitingEntityReceived.
     */
    private boolean deferLoadAwaitingQueryBuilt;

    /** Place to put sticky activity. */
    @UiField
    SimplePanel stickyActivityHolder;

    /** Area containing the sticky activity. */
    @UiField
    DivElement stickyActivityArea;

    /** Views used to load the current stream. */
    List<String> loadedViews = Collections.singletonList("[do not match]");

    /** So results for the wrong stream can be detected and ignored. */
    private String currentStreamRequest;

    /** Search term used to load the current stream. */
    String loadedSearchTerm = "";

    /** Timer to delay progressive search until the user pauses. */
    private final Timer searchTimer = new Timer()
    {
        @Override
        public void run()
        {
            String searchText = searchBox.getText();
            loadStream(Session.getInstance().getUrlViews(), searchText);
            searchUrlTimer.schedule(SEARCH_URL_UPDATE_DELAY);
        }
    };

    /** Timer to update the URL with the search term. */
    private final Timer searchUrlTimer = new Timer()
    {
        @Override
        public void run()
        {
            updateUrlWithSearchTerm();
        }
    };

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

    /**
     * Build the page.
     */
    private void buildPage()
    {
        addEventHandlers();
        addObservers();
        setupStreamsAndBookmarks();
        moreLink.setVisible(false);
        streamSearchStatusWidget.setVisible(false);
        errorPanel.setVisible(false);

        followingFilterPanel = createPanel("Following", "following", "style/images/customStream.png", null, "", "",
                false);
        everyoneFilterPanel = createPanel("Everyone", "everyone", "style/images/customStream.png", null, "", "", false);

        defaultList.add(followingFilterPanel);
        defaultList.add(everyoneFilterPanel);

        final PersonModelView currentPerson = Session.getInstance().getCurrentPerson();

        AvatarLinkPanel userAvatar = new AvatarLinkPanel(currentPerson.getEntityType(), currentPerson.getAccountId(),
                currentPerson.getAvatarId(), Size.Small, currentPerson.getDisplayName());
        userPanel.add(userAvatar);

        FlowPanel userLinkPanel = new FlowPanel();

        String nameUrl = Session.getInstance().generateUrl(
                new CreateUrlRequest(Page.PEOPLE, currentPerson.getAccountId()));
        Hyperlink name = new Hyperlink(currentPerson.getDisplayName(), nameUrl);
        name.setTitle(currentPerson.getDisplayName());
        name.addStyleName(style.currentUserStreamLink());
        name.addStyleName(StaticResourceBundle.INSTANCE.coreCss().ellipsisChild());
        userLinkPanel.add(name);

        String confUrl = Session.getInstance().generateUrl(
                new CreateUrlRequest(Page.PERSONAL_SETTINGS, currentPerson.getAccountId()));
        Hyperlink conf = new Hyperlink("Configure My Stream", confUrl);
        conf.addStyleName(style.currentUserConfLink());
        userLinkPanel.add(conf);

        userPanel.add(userLinkPanel);

        CustomStreamModel.getInstance().fetch(null, true);
        StreamBookmarksModel.getInstance().fetch(null, true);

        moreSpinner.addClassName(StaticResourceBundle.INSTANCE.coreCss().displayNone());
        noResults.addClassName(StaticResourceBundle.INSTANCE.coreCss().displayNone());
        unseenActivityNotificationPanel.setActive(true);
    }

    /**
     * Got activity.
     *
     * @param event
     *            the event.
     */
    private void gotActivity(final GotActivityResponseEvent event)
    {
        streamPanel.clear();
        unseenActivityNotificationPanel.setActive(false);
        activitySpinner.addClassName(StaticResourceBundle.INSTANCE.coreCss().displayNone());

        if (event.getResponse() != null)
        {
            EntityType actorType = event.getResponse().getDestinationStream().getEntityType();
            String actorName = event.getResponse().getDestinationStream().getUniqueId();

            if (actorType.equals(EntityType.GROUP))
            {
                GroupModel.getInstance().fetch(actorName, false);

            }
            else if (actorType.equals(EntityType.PERSON))
            {
                PersonalInformationModel.getInstance().fetch(actorName, false);
            }
        }

        streamPanel.add(new ActivityDetailPanel(event.getResponse(), ShowRecipient.YES));
        streamPanel.removeStyleName(StaticResourceBundle.INSTANCE.coreCss().hidden());
    }

    /**
     * Add events.
     */
    private void addObservers()
    {
        final EventBus eventBus = EventBus.getInstance();
        eventBus.addObserver(GotActivityResponseEvent.class, new Observer<GotActivityResponseEvent>()
        {

            public void update(final GotActivityResponseEvent event)
            {
                gotActivity(event);
            }
        });
        eventBus.addObserver(GotStreamResponseEvent.class, new Observer<GotStreamResponseEvent>()
        {
            public void update(final GotStreamResponseEvent event)
            {
                // throw out results if for the wrong stream (or we don't want the results)
                if (currentStreamRequest == null || !currentStreamRequest.equals(event.getRequest()))
                {
                    return;
                }

                final PagedSet<ActivityDTO> activitySet = event.getStream();
                if (activitySet.getPagedSet().size() > 0)
                {
                    longNewestActivityId = activitySet.getPagedSet().get(0).getEntityId();
                    longOldestActivityId = activitySet.getPagedSet().get(activitySet.getPagedSet().size() - 1)
                            .getEntityId();
                }

                if (StreamJsonRequestFactory.getJSONRequest(event.getJsonRequest()).containsKey("minId"))
                {
                    for (int i = activitySet.getPagedSet().size(); i > 0; i--)
                    {
                        appendActivity(activitySet.getPagedSet().get(i - 1));
                    }
                }
                else if (StreamJsonRequestFactory.getJSONRequest(event.getJsonRequest()).containsKey("maxId"))
                {
                    moreSpinner.addClassName(StaticResourceBundle.INSTANCE.coreCss().displayNone());
                    for (ActivityDTO activity : activitySet.getPagedSet())
                    {
                        streamPanel.add(renderer.render(activity));
                    }
                    moreLink.setVisible(activitySet.getTotal() > activitySet.getPagedSet().size());
                }
                else
                {
                    streamPanel.clear();
                    unseenActivityNotificationPanel.setActive(true);
                    activitySpinner.addClassName(StaticResourceBundle.INSTANCE.coreCss().displayNone());
                    streamPanel.removeStyleName(StaticResourceBundle.INSTANCE.coreCss().hidden());

                    List<ActivityDTO> activities = activitySet.getPagedSet();
                    for (ActivityDTO activity : activities)
                    {
                        streamPanel.add(renderer.render(activity));
                    }
                    if (activities.size() == 0)
                    {
                        noResults.removeClassName(StaticResourceBundle.INSTANCE.coreCss().displayNone());
                    }
                    moreLink.setVisible(activitySet.getTotal() > activities.size());
                }
                if (activitySet.getPagedSet().size() > 0)
                {
                    noResults.addClassName(StaticResourceBundle.INSTANCE.coreCss().displayNone());
                }
            }
        });

        // users are not initially subscribed for emails when following a person/group, so set the status properly (else
        // if you were following and unsubscribed, then re-subscribed, the status would be old and wrong)
        eventBus.addObserver(InsertedPersonFollowerResponseEvent.class,
                new Observer<InsertedPersonFollowerResponseEvent>()
                {
                    public void update(final InsertedPersonFollowerResponseEvent ev)
                    {
                        setSubscribeStatus(false);
                    }
                });

        eventBus.addObserver(InsertedGroupMemberResponseEvent.class, new Observer<InsertedGroupMemberResponseEvent>()
        {
            public void update(final InsertedGroupMemberResponseEvent ev)
            {
                setSubscribeStatus(false);
            }
        });

        eventBus.addObserver(GotPersonFollowerStatusResponseEvent.class,
                new Observer<GotPersonFollowerStatusResponseEvent>()
                {
                    public void update(final GotPersonFollowerStatusResponseEvent event)
                    {
                        subscribeViaEmail.setVisible(event.getResponse().equals(FollowerStatus.FOLLOWING));
                    }
                });

        eventBus.addObserver(HistoryViewsChangedEvent.class, new Observer<HistoryViewsChangedEvent>()
        {
            public void update(final HistoryViewsChangedEvent event)
            {
                searchTimer.cancel();
                searchUrlTimer.cancel();
                handleViewsChanged(event.getViews());
                final String searchText = Session.getInstance().getParameterValue("search");
                if (!searchBox.getText().equals(searchText))
                {
                    searchBox.setText(searchText);
                }
            }
        });

        eventBus.addObserver(MessageStreamAppendEvent.class, new Observer<MessageStreamAppendEvent>()
        {
            public void update(final MessageStreamAppendEvent event)
            {
                longNewestActivityId = event.getMessage().getId();

                if (sortKeyword.equals("date"))
                {
                    appendActivity(event.getMessage());
                    noResults.addClassName(StaticResourceBundle.INSTANCE.coreCss().displayNone());
                }
                else
                {
                    recentSort.getElement().dispatchEvent(
                            Document.get().createClickEvent(1, 0, 0, 0, 0, false, false, false, false));
                }

            }
        });

        eventBus.addObserver(CustomStreamCreatedEvent.class, new Observer<CustomStreamCreatedEvent>()
        {
            public void update(final CustomStreamCreatedEvent event)
            {
                CustomStreamModel.getInstance().fetch(null, true);
            }
        });

        eventBus.addObserver(CustomStreamDeletedEvent.class, new Observer<CustomStreamDeletedEvent>()
        {
            public void update(final CustomStreamDeletedEvent event)
            {
                CustomStreamModel.getInstance().fetch(null, true);
            }
        });

        eventBus.addObserver(CustomStreamUpdatedEvent.class, new Observer<CustomStreamUpdatedEvent>()
        {
            public void update(final CustomStreamUpdatedEvent event)
            {
                CustomStreamModel.getInstance().fetch(null, true);
            }
        });

        eventBus.addObserver(StreamReinitializeRequestEvent.class, new Observer<StreamReinitializeRequestEvent>()
        {
            public void update(final StreamReinitializeRequestEvent event)
            {
                loadStream(Session.getInstance().getUrlViews(), Session.getInstance().getParameterValue("search"));
            }
        });

        eventBus.addObserver(UpdatedHistoryParametersEvent.class, new Observer<UpdatedHistoryParametersEvent>()
        {
            public void update(final UpdatedHistoryParametersEvent event)
            {
                searchTimer.cancel();
                searchUrlTimer.cancel();
                if (!event.getViewChanged())
                {
                    handleViewsChanged(Session.getInstance().getUrlViews());
                }
            }
        });

        eventBus.addObserver(AddedFeaturedStreamResponseEvent.class, new Observer<AddedFeaturedStreamResponseEvent>()
        {
            public void update(final AddedFeaturedStreamResponseEvent event)
            {
                eventBus.notifyObservers(new ShowNotificationEvent(new Notification("Stream has been featured.")));
            }
        });

        eventBus.addObserver(UpdatedGroupStickyActivityEvent.class, new Observer<UpdatedGroupStickyActivityEvent>()
        {
            public void update(final UpdatedGroupStickyActivityEvent ev)
            {
                // make sure event applies to the current view (since current view may not even be a group)
                if (currentStreamEntity != null && currentStreamEntity.getEntityType() == EntityType.GROUP
                        && ev.getGroupId() == currentStreamEntity.getEntityId() && !singleActivityMode)
                {
                    if (ev.getActivity() == null)
                    {
                        stickyActivityHolder.clear();
                        UIObject.setVisible(stickyActivityArea, false);
                    }
                    else
                    {
                        stickyActivityHolder.clear();
                        stickyActivityHolder.add(stickyActivityRenderer.render(ev.getActivity()));
                        UIObject.setVisible(stickyActivityArea, true);
                    }

                    // reload the stream to get prior sticky activities back in it (and a freshly stuck activity out)
                    eventBus.notifyObservers(StreamReinitializeRequestEvent.getEvent());
                }
            }
        });

        addEntityObservers();
    }

    /**
     * Add entity observers.
     */
    private void addEntityObservers()
    {
        EventBus.getInstance().addObserver(GotPersonalInformationResponseEvent.class,
                new Observer<GotPersonalInformationResponseEvent>()
                {
                    public void update(final GotPersonalInformationResponseEvent event)
                    {
                        PersonModelView person = event.getResponse();
                        currentDisplayName = person.getDisplayName();
                        currentScopeId = person.getStreamId();

                        if (person.isAccountLocked())
                        {
                            streamOptionsPanel.getStyle().setDisplay(Display.NONE);
                            currentStream.setScopeType(null);
                            errorPanel.clear();
                            errorPanel.setVisible(true);
                            activitySpinner.addClassName(StaticResourceBundle.INSTANCE.coreCss().displayNone());
                            errorPanel.add(new Label("Employee profile not found"));
                            errorPanel.add(new Label(LOCKED_USER_TEXT));
                            streamPanel.removeStyleName(StaticResourceBundle.INSTANCE.coreCss().hidden());

                            streamDetailsComposite.setVisible(false);
                            currentStream.setScopeType(null);

                            // block display of activities
                            currentStreamRequest = null;
                            streamPanel.clear();
                            unseenActivityNotificationPanel.setActive(false);
                            activitySpinner.addClassName(StaticResourceBundle.INSTANCE.coreCss().displayNone());
                            streamPanel.addStyleName(StaticResourceBundle.INSTANCE.coreCss().hidden());
                            noResults.addClassName(StaticResourceBundle.INSTANCE.coreCss().displayNone());
                            moreLink.setVisible(false);
                        }
                        else
                        {
                            currentStream.setDisplayName(person.getDisplayName());
                            streamDetailsComposite.setVisible(true);
                        }
                        if (!person.isStreamPostable()
                                && !person.getAccountId().equals(
                                        Session.getInstance().getCurrentPerson().getAccountId()))
                        {
                            currentStream.setScopeType(null);
                        }
                        /*if (currentStream.getScopeType() != null)
                        {
                            getEmailContactLink.setHref("/resources/emailcontact/stream/person/" + person.getId());
                            getEmailContactLink.setVisible(true);
                        }*/
                        if (!singleActivityMode)
                        {
                            EventBus.getInstance().notifyObservers(new PostableStreamScopeChangeEvent(currentStream));
                        }
                    }
                });

        EventBus.getInstance().addObserver(GotGroupModelViewInformationResponseEvent.class,
                new Observer<GotGroupModelViewInformationResponseEvent>()
                {
                    public void update(final GotGroupModelViewInformationResponseEvent event)
                    {
                        onGroupModelViewReceived(event.getResponse());
                    }
                });
    }

    /**
     * Processing when group information is received.
     *
     * @param group
     *            The group.
     */
    private void onGroupModelViewReceived(final DomainGroupModelView group)
    {
      // If group is null, then that means that the group
      // was not found. As a result, a "Group not found"
      // page should be displayed.
      // This page is very similar to the "Activity not found" page
      if (group == null)
      { 
        showGroupNotFoundPage();
      }
      else
      {
        currentDisplayName = group.getDisplayName();
          currentScopeId = group.getStreamId();
 
          currentStream.setDisplayName(group.getName());
          streamDetailsComposite.setVisible(true);
 
          if (group.isRestricted())
          {
              streamOptionsPanel.getStyle().setDisplay(Display.NONE);
              currentStream.setScopeType(null);
              postBox.setVisible(false);
 
              errorPanel.clear();
              errorPanel.setVisible(true);
              activitySpinner.addClassName(StaticResourceBundle.INSTANCE.coreCss().displayNone());
              errorPanel.add(new Label("Access to this group is restricted"));
              errorPanel.add(new Label("To view this group's stream please request access from its coordinator"));
 
              final SpinnerLabelButton button = new SpinnerLabelButton(new ClickHandler()
              {
                  public void onClick(final ClickEvent inArg0)
                  {
                      GroupMembershipRequestModel.getInstance().insert(group.getShortName());
                  }
              });
 
              EventBus.getInstance().addObserver(InsertedRequestForGroupMembershipResponseEvent.class,
                      new Observer<InsertedRequestForGroupMembershipResponseEvent>()
                      {
                          public void update(final InsertedRequestForGroupMembershipResponseEvent inArg1)
                          {
                              button.disable();
                              EventBus.getInstance()
                                      .notifyObservers(
                                              new ShowNotificationEvent(new Notification(
                                                      "Your request for access has been sent")));
                          }
                      });
 
              button.addStyleName(StaticResourceBundle.INSTANCE.coreCss().requestAccessButton());
              errorPanel.add(button);
 
              streamPanel.clear();
              unseenActivityNotificationPanel.setActive(true);
          }
          else
          {
              currentStreamEntity = group;
 
              if (group.getStickyActivity() != null && !singleActivityMode
                      && (loadedSearchTerm == null || loadedSearchTerm == ""))
              {
                  stickyActivityHolder.add(stickyActivityRenderer.render(group.getStickyActivity()));
                  UIObject.setVisible(stickyActivityArea, true);
              }
          }
          boolean isCoordinator = false;
 
          for (PersonModelView coordinator : group.getCoordinators())
          {
              AvatarBadgeManager.getInstance().setBadge(style.streamContainerPanel(), coordinator.getUniqueId());
              if (coordinator.getAccountId().equals(Session.getInstance().getCurrentPerson().getAccountId()))
              {
                  isCoordinator = true;
              }
          }
          if (!group.isStreamPostable() && !isCoordinator)
          {
              currentStream.setScopeType(null);
          }
          /*else
          {
              getEmailContactLink.setHref("/resources/emailcontact/stream/group/" + group.getId());
              getEmailContactLink.setVisible(true);
          }*/
 
          if (!singleActivityMode)
          {
              if (Session.getInstance().getCurrentPersonRoles().contains(Role.SYSTEM_ADMIN) || isCoordinator)
              {
                  streamContainerPanel.addStyleName(StaticResourceBundle.INSTANCE.coreCss().hasOwnerRights());
              }
 
              EventBus.getInstance().notifyObservers(new PostableStreamScopeChangeEvent(currentStream));
          }
 
          if (deferLoadAwaitingEntityReceived)
          {
              deferLoadAwaitingEntityReceived = false;
              currentRequestObj = StreamJsonRequestFactory.setExcludeId(group.getStickyActivityId(),
                  currentRequestObj);
              if (!deferLoadAwaitingQueryBuilt)
              {
                  fetchStream(currentRequestObj);
              }
          }
      }
    }
   
    /**
     * Shows a "not found" message.
     */
    private void showGroupNotFoundPage()
    {
      activitySpinner.addClassName(StaticResourceBundle.INSTANCE.coreCss().displayNone());
      streamPanel.clear();
    errorPanel.clear();
    postBox.setVisible(false);
   
      Panel errorReport = new FlowPanel();
        errorReport.addStyleName(StaticResourceBundle.INSTANCE.coreCss().warningReport());

        FlowPanel centeringPanel = new FlowPanel();
        centeringPanel.addStyleName(StaticResourceBundle.INSTANCE.coreCss().warningReportContainer());
        centeringPanel.add(errorReport);
        streamPanel.add(centeringPanel);

        FlowPanel msgPanel = new FlowPanel();

        Label msgHeader = new Label("Group not found");
        msgHeader.addStyleName(StaticResourceBundle.INSTANCE.coreCss().warningMessage());

        Label msgText = new Label("The group you were looking for has been deleted or could not be found.");
        FlowPanel text = new FlowPanel();
        text.add(msgText);
        text.addStyleName(StaticResourceBundle.INSTANCE.coreCss().errorMessageText());

        msgPanel.add(msgHeader);
        msgPanel.add(msgText);

        streamPanel.removeStyleName(StaticResourceBundle.INSTANCE.coreCss().hidden());
       
    streamPanel.setVisible(true);
    streamContainerPanel.setVisible(true);
        errorReport.add(msgPanel);
    errorPanel.setVisible(true);
    errorReport.setVisible(true);
    centeringPanel.setVisible(true);
    msgPanel.setVisible(true);
    }
   
    /**
     * Handle views changed.
     *
     * @param inViews
     *            the views.
     */
    protected void handleViewsChanged(final List<String> inViews)
    {
        String search = Session.getInstance().getParameterValue("search");

        // prevent reloading the same content
        if (!loadedViews.equals(inViews) || !loadedSearchTerm.equals(search))
        {
            loadStream(inViews, search);
        }
        List<String> views = new ArrayList<String>(inViews);

        if (views.size() < 2 || !"sort".equals(views.get(views.size() - 2)))
        {
            views.add("sort");
            views.add("recent");
        }

        Map<String, String> params = (search == null || search.isEmpty()) ? Collections.EMPTY_MAP : Collections
                .singletonMap("search", search);

        views.set(views.size() - 1, "recent");
        recentSort.setTargetHistoryToken(Session.getInstance().generateUrl(
                new CreateUrlRequest(Page.ACTIVITY, views, params)));

        views.set(views.size() - 1, "popular");
        popularSort.setTargetHistoryToken(Session.getInstance().generateUrl(
                new CreateUrlRequest(Page.ACTIVITY, views, params)));

        views.set(views.size() - 1, "active");
        activeSort.setTargetHistoryToken(Session.getInstance().generateUrl(
                new CreateUrlRequest(Page.ACTIVITY, views, params)));
    }

    /**
     * Setup bookmarks and custom streams.
     */
    private void setupStreamsAndBookmarks()
    {
        final EventBus eventBus = EventBus.getInstance();

        eventBus.addObserver(GotCurrentUserStreamBookmarks.class, new Observer<GotCurrentUserStreamBookmarks>()
        {
            private final AvatarUrlGenerator groupUrlGen = new AvatarUrlGenerator(EntityType.GROUP);
            private final AvatarUrlGenerator personUrlGen = new AvatarUrlGenerator(EntityType.PERSON);

            public void update(final GotCurrentUserStreamBookmarks event)
            {
                bookmarkList.clear();
                bookmarksWidgetMap.clear();

                List<StreamFilter> sortedStreamFilters = event.getResponse();
                Collections.sort(sortedStreamFilters, new StreamFilterNameComparator());

                for (final StreamFilter filter : sortedStreamFilters)
                {
                    JSONObject req = StreamJsonRequestFactory.getJSONRequest(filter.getRequest());
                    String uniqueId = null;
                    String entityType = null;

                    String imgUrl = "";

                    if (req.containsKey("query"))
                    {
                        JSONObject query = req.get("query").isObject();
                        if (query.containsKey(StreamJsonRequestFactory.RECIPIENT_KEY))
                        {
                            JSONArray recipient = query.get(StreamJsonRequestFactory.RECIPIENT_KEY).isArray();
                            if (recipient.size() > 0)
                            {
                                JSONObject recipientObj = recipient.get(0).isObject();
                                uniqueId = recipientObj.get("name").isString().stringValue();
                                entityType = recipientObj.get("type").isString().stringValue().toLowerCase();

                                AvatarUrlGenerator urlGen = groupUrlGen;

                                if ("person".equals(entityType))
                                {
                                    urlGen = personUrlGen;
                                }

                                imgUrl = urlGen.getSmallAvatarUrl(filter.getOwnerAvatarId());

                            }
                        }

                    }

                    if (uniqueId != null && entityType != null)
                    {
                        String bookmarkUrl = entityType + "/" + uniqueId;
                        StreamNamePanel bookmarkFilter = createPanel(filter.getName(), bookmarkUrl, imgUrl,
                                new ClickHandler()
                                {
                                    public void onClick(final ClickEvent event)
                                    {
                                        if (new WidgetJSNIFacadeImpl()
                                                .confirm("Are you sure you want to delete this bookmark?"))
                                        {
                                            StreamBookmarksModel.getInstance().delete(filter.getId());
                                        }

                                        event.stopPropagation();
                                    }
                                }, style.deleteBookmark(), "", true);

                        bookmarkList.add(bookmarkFilter);
                        bookmarksWidgetMap.put(bookmarkUrl, bookmarkFilter);
                    }

                }
                if (sortedStreamFilters.size() == 0)
                {
                    Label defaultLabel = new Label("Bookmarks allow you to quickly jump to any stream in Eureka.");
                    defaultLabel.addStyleName(style.noBookmarksMessage());
                    bookmarkList.add(defaultLabel);
                }

                bookmarksLoaded = true;
                checkInit();
            }
        });

        eventBus.addObserver(StreamActivitySubscriptionChangedEvent.class,
                new Observer<StreamActivitySubscriptionChangedEvent>()
                {
                    public void update(final StreamActivitySubscriptionChangedEvent ev)
                    {
                        boolean newStatus = ev.getResponse().getReceiveNewActivityNotifications();
                        setSubscribeStatus(newStatus);

                        String msg = newStatus ? "You will now receive emails for new activities to this stream"
                                : "You will no longer receive emails for new activities to this stream";
                        eventBus.notifyObservers(new ShowNotificationEvent(new Notification(msg)));
                    }
                });
        eventBus.addObserver(GotStreamActivitySubscriptionResponseEvent.class,
                new Observer<GotStreamActivitySubscriptionResponseEvent>()
                {
                    public void update(final GotStreamActivitySubscriptionResponseEvent result)
                    {
                        setSubscribeStatus(result.isSubscribed());
                    }
                });

        eventBus.addObserver(GotCurrentUserCustomStreamsResponseEvent.class,
                new Observer<GotCurrentUserCustomStreamsResponseEvent>()
                {
                    public void update(final GotCurrentUserCustomStreamsResponseEvent event)
                    {
                        filterList.clear();
                        customStreamWidgetMap.clear();

                        StreamNamePanel savedBy = createPanel("My Saved Items", "custom/0/"
                                + "{\"query\":{\"savedBy\":\""
                                + Session.getInstance().getCurrentPerson().getAccountId() + "\"}}",
                                "style/images/customStream.png", null, "", "", false);

                        filterList.add(savedBy);
                        customStreamWidgetMap.put(0L, savedBy);

                        StreamNamePanel likedBy = createPanel("My Liked Items", "custom/1/"
                                + "{\"query\":{\"likedBy\":[{\"type\":\"PERSON\", \"name\":\""
                                + Session.getInstance().getCurrentPerson().getAccountId() + "\"}]}}/My Liked Items",
                                "style/images/customStream.png", null, "", "", false);

                        filterList.add(likedBy);
                        customStreamWidgetMap.put(1L, likedBy);

                        for (final StreamFilter filter : event.getResponse().getStreamFilters())
                        {
                            StreamNamePanel filterPanel = createPanel(
                                    filter.getName(),
                                    "custom/"
                                            + filter.getId()
                                            + "/"
                                            + URL.encodeComponent(filter.getRequest().
                                                        replace("%%CURRENT_USER_ACCOUNT_ID%%",
                                                            Session.getInstance().getCurrentPerson().getAccountId())),
                                    "style/images/customStream.png", new ClickHandler()
                                    {

                                        public void onClick(final ClickEvent event)
                                        {
                                            Dialog.showCentered(new CustomStreamDialogContent((Stream) filter));
                                            event.stopPropagation();
                                        }
                                    }, style.editCustomStream(), "edit", false);

                            filterList.add(filterPanel);
                            customStreamWidgetMap.put(filter.getId(), filterPanel);
                        }

                        customStreamsLoaded = true;
                        checkInit();
                    }
                });

        eventBus.addObserver(StreamSearchBeginEvent.class, new Observer<StreamSearchBeginEvent>()
        {
            public void update(final StreamSearchBeginEvent event)
            {
                if (null == event.getSearchText())
                {
                    eventBus.notifyObservers(new UpdateHistoryEvent(new CreateUrlRequest("search", "", false)));
                    searchBox.setText("");
                }
            }
        });
    }

    /**
     * Add events.
     */
    private void addEventHandlers()
    {
        moreLink.addClickHandler(new ClickHandler()
        {
            public void onClick(final ClickEvent event)
            {
                moreSpinner.removeClassName(StaticResourceBundle.INSTANCE.coreCss().displayNone());

                JSONObject moreItemsRequest = StreamJsonRequestFactory.setMaxId(longOldestActivityId,
                        StreamJsonRequestFactory.getJSONRequest(currentRequestObj.toString()));

                fetchStream(moreItemsRequest);
            }
        });

        searchBox.addKeyUpHandler(new KeyUpHandler()
        {
            private int lastSearchLength = 0;

            public void onKeyUp(final KeyUpEvent event)
            {
                final String searchText = searchBox.getText();
                final int searchTextLength = searchText.length();
                if (event.getNativeKeyCode() == KeyCodes.KEY_ENTER)
                {
                    lastSearchLength = searchTextLength;

                    searchTimer.cancel();
                    searchUrlTimer.cancel();

                    // don't load stream here - the URL change will cause it to be reloaded

                    EventBus.getInstance().notifyObservers(
                            new UpdateHistoryEvent(new CreateUrlRequest("search", searchText, false)));
                }
                else if ((searchTextLength > 2 && searchTextLength != lastSearchLength)
                        || searchTextLength < lastSearchLength)
                {
                    lastSearchLength = searchTextLength;

                    searchTimer.schedule(SEARCH_UPDATE_DELAY);
                }
            }
        });

        searchBox.addBlurHandler(new BlurHandler()
        {
            public void onBlur(final BlurEvent inEvent)
            {
                updateUrlWithSearchTerm();
            }
        });

        addBookmark.addClickHandler(new ClickHandler()
        {
            public void onClick(final ClickEvent event)
            {
                StreamBookmarksModel.getInstance().insert(currentScopeId);
                addBookmark.setVisible(false);
                EventBus.getInstance().notifyObservers(
                        new ShowNotificationEvent(new Notification("You have bookmarked this stream.")));
            }
        });

        subscribeViaEmail.addClickHandler(new ClickHandler()
        {
            public void onClick(final ClickEvent event)
            {
                if (!isSubscribed)
                {
                    if (currentStream.getScopeType().equals(ScopeType.GROUP))
                    {
                        Dialog.showCentered(new GroupEmailSubscribeOptionsDialogContent(currentStream.getUniqueKey()));
                    }
                    else if (currentStream.getScopeType().equals(ScopeType.PERSON))
                    {
                        PersonActivitySubscriptionModel.getInstance().insert(currentStream.getUniqueKey());
                        setSubscribeStatus(true);
                    }
                }
                else
                {
                    Deletable<String> deletable = null;

                    if (currentStream.getScopeType().equals(ScopeType.GROUP))
                    {
                        deletable = GroupActivitySubscriptionModel.getInstance();
                    }
                    else if (currentStream.getScopeType().equals(ScopeType.PERSON))
                    {
                        deletable = PersonActivitySubscriptionModel.getInstance();
                    }

                    if (deletable != null)
                    {
                        deletable.delete(currentStream.getUniqueKey());
                        setSubscribeStatus(false);
                    }
                }
            }
        });

        addToStartPage.addClickHandler(new ClickHandler()
        {
            public void onClick(final ClickEvent event)
            {
                // For the app's location, use the current URL minus a few parameters we know we don't want. (They are
                // used by other lists, but get left in the URL when switching tabs.)
                // We don't build the URL from the stream id, since that doesn't take search terms into account.
                HashMap<String, String> params = new HashMap<String, String>();
                params.put("listId", null);
                params.put("listFilter", null);
                params.put("listSort", null);
                params.put("startIndex", null);
                params.put("endIndex", null);
                String url = Session.getInstance().generateUrl(new CreateUrlRequest(params));

                // make a version of the query that doesn't have the exclusion list
                JSONObject request = currentRequestObj;
                if (request.containsKey("exclude"))
                {
                    request = new JSONObject();
                    for (String key : currentRequestObj.keySet())
                    {
                        if (!"exclude".equals(key))
                        {
                            request.put(key, currentRequestObj.get(key));
                        }
                    }
                }
                // TODO: get correct title from somewhere.
                String prefs = "{\"streamQuery\":"
                        + makeJsonString(STREAM_URL_TRANSFORMER.getUrl(null, request.toString()))
                        + ",\"gadgetTitle\":" + makeJsonString(currentDisplayName) + ",\"streamLocation\":"
                        + makeJsonString(url) + "}";

                GadgetModel.getInstance().insert(
                        new AddGadgetToStartPageRequest("{d7a58391-5375-4c76-b5fc-a431c42a7555}", null, prefs));
                EventBus.getInstance()
                        .notifyObservers(
                                new ShowNotificationEvent(new Notification(
                                        "This stream will now show up on your start page.")));
            }
        });

        createFilter.addClickHandler(new ClickHandler()
        {
            public void onClick(final ClickEvent event)
            {
                Dialog.showCentered(new CustomStreamDialogContent());
            }
        });

        feedLink.addClickHandler(new ClickHandler()
        {
            public void onClick(final ClickEvent event)
            {
                Window.Location.assign("/resources/atom/stream/query/recipient/" + currentStream.getScopeType() + ":"
                        + currentStream.getUniqueKey());
            }
        });
    }

    /**
     * Requests a stream via the model and tracks it for proper reciept matching.
     *
     * @param request
     *            The request in JSON form.
     */
    private void fetchStream(final JSONObject request)
    {
        currentStreamRequest = request.toString();
        StreamModel.getInstance().fetch(currentStreamRequest, false);
    }

    /**
     * Updates the URL to include the search term.
     */
    private void updateUrlWithSearchTerm()
    {
        String searchText = searchBox.getText();
        String searchParam = Session.getInstance().getParameterValue("search");
        if (!searchText.equals(searchParam))
        {
            EventBus.getInstance().notifyObservers(
                    new UpdateHistoryEvent(new CreateUrlRequest("search", searchText, false)));
        }
    }

    /**
     * Update subscription status consistently.
     *
     * @param inIsSubscribed
     *            New status.
     */
    private void setSubscribeStatus(final boolean inIsSubscribed)
    {
        isSubscribed = inIsSubscribed;
        subscribeViaEmail.setText(isSubscribed ? "Unsubscribe to Emails" : "Subscribe via Email");
    }

    /**
     * Creates the JSON representation of a string value. (Escapes characters and adds string delimiters or returns null
     * keyword as applicable.) See http://www.json.org/ for syntax. Assumes the string contains no control characters.
     *
     * @param input
     *            Input string, possibly null.
     * @return JSON string representation.
     */
    private static native String makeJsonString(final String input)
    /*-{
     return input == null ? 'null' : '"' + input.replace(/\\/g,'\\\\').replace(/"/g,'\\"') + '"';
     }-*/;

    /**
     * Append a new message.
     *
     * @param message
     *            the messa.ge
     */
    private void appendActivity(final ActivityDTO message)
    {
        Panel newActivity = renderer.render(message);
        newActivity.setVisible(false);
        streamPanel.insert(newActivity, 0);
        EffectsFacade.nativeFadeIn(newActivity.getElement(), true);
    }

    /**
     * Load a stream.
     *
     * @param views
     *            the stream history link.
     * @param searchTerm
     *            the search term.
     */
    private void loadStream(final List<String> views, final String searchTerm)
    {
        // save for change detection
        loadedViews = new ArrayList<String>(views);
        loadedSearchTerm = searchTerm;

        Window.scrollTo(0, 0);
        noResults.addClassName(StaticResourceBundle.INSTANCE.coreCss().displayNone());
        Session.getInstance().getActionProcessor().setQueueRequests(true);

        addBookmark.setVisible(false);
        subscribeViaEmail.setVisible(false);
        feedLink.setVisible(false);

        streamOptionsPanel.getStyle().setDisplay(Display.BLOCK);
        streamDetailsComposite.init();

        errorPanel.clear();
        errorPanel.setVisible(false);

        AvatarBadgeManager.getInstance().clearBadges();

        singleActivityMode = false;
        activitySpinner.removeClassName(StaticResourceBundle.INSTANCE.coreCss().displayNone());
        moreLink.setVisible(false);
        streamPanel.addStyleName(StaticResourceBundle.INSTANCE.coreCss().hidden());
        currentRequestObj = StreamJsonRequestFactory.getEmptyRequest();
        currentStream = new StreamScope(ScopeType.PERSON, Session.getInstance().getCurrentPerson().getAccountId());
        ShowRecipient showRecipient = ShowRecipient.YES;

        streamContainerPanel.removeStyleName(StaticResourceBundle.INSTANCE.coreCss().hasOwnerRights());

        stickyActivityHolder.clear();
        UIObject.setVisible(stickyActivityArea, false);

        renderer.setShowStickActivity(false);

        currentStreamEntity = null;
        deferLoadAwaitingQueryBuilt = false;
        deferLoadAwaitingEntityReceived = false;

        //getEmailContactLink.setVisible(false);

        boolean streamIsAnEntity = false;

        if (views == null || views.size() == 0 || views.get(0).equals("following")
                || ((views.get(0).equals("sort") && (views.size() == 2))))
        {
            currentRequestObj = StreamJsonRequestFactory.setSourceAsFollowing(currentRequestObj);
            setAsActiveStream(followingFilterPanel);
            EventBus.getInstance().notifyObservers(new PostableStreamScopeChangeEvent(currentStream));
            feedLink.setVisible(true);
            streamDetailsComposite.setStreamTitle("Following", CustomAvatar.FOLLOWING);
            streamDetailsComposite.setCondensedMode(true);
            currentDisplayName = "Following";
        }
        else if (views.get(0).equals("person") && views.size() >= 2)
        {
            streamIsAnEntity = true;
            showRecipient = ShowRecipient.RESOURCE_ONLY;
            String accountId = views.get(1);
            currentRequestObj = StreamJsonRequestFactory.addRecipient(EntityType.PERSON, accountId, currentRequestObj);
            PersonalInformationModel.getInstance().fetch(accountId, false);
            currentStream.setScopeType(ScopeType.PERSON);
            currentStream.setUniqueKey(accountId);
            String mapKey = "person/" + accountId;
            setAsActiveStream(bookmarksWidgetMap.get(mapKey));
            if (!bookmarksWidgetMap.containsKey(mapKey))
            {
                addBookmark.setVisible(true);
            }
            else
            {
                addBookmark.setVisible(false);
                currentDisplayName = bookmarksWidgetMap.get(mapKey).getStreamName();
            }
            subscribeViaEmail.setVisible(true);
            feedLink.setVisible(true);
            streamDetailsComposite.setCondensedMode(false);

            PersonActivitySubscriptionModel.getInstance().fetch(currentStream.getUniqueKey(), true);
        }
        else if (views.get(0).equals("group") && views.size() >= 2)
        {
            streamIsAnEntity = true;
            showRecipient = ShowRecipient.RESOURCE_ONLY;
            String shortName = views.get(1);
            currentRequestObj = StreamJsonRequestFactory.addRecipient(EntityType.GROUP, shortName, currentRequestObj);
            GroupModel.getInstance().fetch(shortName, false);
            currentStream.setScopeType(ScopeType.GROUP);
            currentStream.setUniqueKey(shortName);
            String mapKey = "group/" + shortName;
            setAsActiveStream(bookmarksWidgetMap.get(mapKey));
            if (!bookmarksWidgetMap.containsKey(mapKey))
            {
                addBookmark.setVisible(true);
            }
            else
            {
                addBookmark.setVisible(false);
                currentDisplayName = bookmarksWidgetMap.get(mapKey).getStreamName();
            }
            subscribeViaEmail.setVisible(true);
            feedLink.setVisible(true);
            streamDetailsComposite.setCondensedMode(false);

            // Note: the links this will generate will only be visible if user is an admin/coordinator (via CSS)
            renderer.setShowStickActivity(true);

            deferLoadAwaitingEntityReceived = true;
            deferLoadAwaitingQueryBuilt = true;

            GroupActivitySubscriptionModel.getInstance().fetch(currentStream.getUniqueKey(), true);
        }
        else if (views.get(0).equals("custom") && views.size() >= 3)
        {
            currentRequestObj = StreamJsonRequestFactory.getJSONRequest(URL.decodeComponent(views.get(2)));
            setAsActiveStream(customStreamWidgetMap.get(Long.parseLong(views.get(1))));
            currentStream.setScopeType(null);
            EventBus.getInstance().notifyObservers(new PostableStreamScopeChangeEvent(currentStream));
            feedLink.setVisible(true);
            String streamName = customStreamWidgetMap.get(Long.parseLong(views.get(1))).getStreamName();
            streamDetailsComposite.setStreamTitle(streamName, CustomAvatar.FOLLOWING);
            streamDetailsComposite.setCondensedMode(true);
            currentDisplayName = streamName;
        }
        else if (views.get(0).equals("everyone"))
        {
            currentRequestObj = StreamJsonRequestFactory.getEmptyRequest();
            setAsActiveStream(everyoneFilterPanel);
            EventBus.getInstance().notifyObservers(new PostableStreamScopeChangeEvent(currentStream));
            feedLink.setVisible(true);
            streamDetailsComposite.setStreamTitle("Everyone", CustomAvatar.EVERYONE);
            streamDetailsComposite.setCondensedMode(true);
            currentDisplayName = "Everyone";
        }
        else if (views.size() == 1)
        {
            showRecipient = ShowRecipient.RESOURCE_ONLY;
            singleActivityMode = true;
        }

        if (searchTerm != null && searchTerm.length() > 0)
        {
            streamSearchStatusWidget.setSearchTerm(searchTerm);
            currentRequestObj = StreamJsonRequestFactory.setSearchTerm(searchTerm, currentRequestObj);
            searchContainer.addClassName(style.activeSearch());
            searchBox.setText(searchTerm);
        }
        else
        {
            streamSearchStatusWidget.onSearchCanceled();
            searchContainer.removeClassName(style.activeSearch());
        }

        if (!streamIsAnEntity)
        {
            streamDetailsComposite.setVisible(true);
        }
        renderer.setShowRecipientInStream(showRecipient);

        if (!singleActivityMode)
        {
            streamOptionsPanel.getStyle().setDisplay(Display.BLOCK);

            String sortBy = "recent";

            if (views != null && views.size() >= 2 && "sort".equals(views.get(views.size() - 2)))
            {
                sortBy = views.get(views.size() - 1);
            }

            recentSort.removeStyleName(style.activeSort());
            popularSort.removeStyleName(style.activeSort());
            activeSort.removeStyleName(style.activeSort());

            sortKeyword = "date";

            if ("recent".equals(sortBy))
            {
                recentSort.addStyleName(style.activeSort());
                sortKeyword = "date";
            }
            else if ("popular".equals(sortBy))
            {
                popularSort.addStyleName(style.activeSort());
                sortKeyword = "interesting";
            }
            else if ("active".equals(sortBy))
            {
                activeSort.addStyleName(style.activeSort());
                sortKeyword = "commentdate";
            }

            currentRequestObj = StreamJsonRequestFactory.setSort(sortKeyword, currentRequestObj);

            // see notes where field declared
            deferLoadAwaitingQueryBuilt = false;
            if (!deferLoadAwaitingEntityReceived)
            {
                fetchStream(currentRequestObj);
            }
        }
        else
        {
            streamDetailsComposite.setCondensedMode(false);
            streamOptionsPanel.getStyle().setDisplay(Display.NONE);
            postBox.setVisible(false);

            try
            {
                ActivityModel.getInstance().fetch(Long.parseLong(views.get(0)), true);
            }
            catch (Exception e)
            {
                // Do nothing.
                int x = 0;
            }
        }

        Session.getInstance().getActionProcessor().fireQueuedRequests();
        Session.getInstance().getActionProcessor().setQueueRequests(false);
    }

    /**
     * Set a stream as active.
     *
     * @param panel
     *            the panel.
     */
    private void setAsActiveStream(final Panel panel)
    {
        Scheduler.get().scheduleDeferred(new ScheduledCommand()
        {
            public void execute()
            {
                if (currentlyActiveStream != null)
                {
                    currentlyActiveStream.removeStyleName(style.activeStream());
                }

                if (panel != null)
                {
                    currentlyActiveStream = panel;
                    panel.addStyleName(style.activeStream());
                }
            }
        });

    }

    /**
     * Check if we should init.
     */
    private void checkInit()
    {
        if (bookmarksLoaded && customStreamsLoaded && !hasInited)
        {
            hasInited = true;
            handleViewsChanged(Session.getInstance().getUrlViews());
        }
    }

    /**
     * Create LI Element for stream lists.
     *
     * @param name
     *            the name of the item.
     * @param view
     *            the history token to load.
     * @param modifyClickHandler
     *            click handler for modify.
     * @param modifyClass
     *            the class for the modify button.
     * @param modifyText
     *            the text for the modify button.
     * @param imgUrl
     *            the img url.
     * @param isAvatar
     *            if the image is an avatar.
     * @return the LI.
     */
    private StreamNamePanel createPanel(final String name, final String view, final String imgUrl,
            final ClickHandler modifyClickHandler, final String modifyClass, final String modifyText,
            final boolean isAvatar)
    {
        StreamNamePanel panel = new StreamNamePanel(name);
        panel.addStyleName(style.streamOptionChild());
        panel.addClickHandler(new ClickHandler()
        {
            public void onClick(final ClickEvent event)
            {
                History.newItem(Session.getInstance().generateUrl(new CreateUrlRequest(Page.ACTIVITY, view)));
            }
        });

        FlowPanel innerPanel = new FlowPanel();

        Image streamImage = new Image(imgUrl);
        if (isAvatar)
        {
            streamImage.addStyleName(style.smallAvatar());
        }

        innerPanel.add(streamImage);

        Label streamName = new Label(name);
        streamName.setTitle(name);
        streamName.addStyleName(style.streamName());
        streamName.addStyleName(StaticResourceBundle.INSTANCE.coreCss().ellipsis());
        innerPanel.add(streamName);

        if (modifyClickHandler != null)
        {
            Label modifyLink = new Label(modifyText);
            modifyLink.addStyleName(modifyClass);
            modifyLink.addClickHandler(modifyClickHandler);
            innerPanel.add(modifyLink);
        }

        panel.add(innerPanel);

        return panel;
    }

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

Related Classes of org.eurekastreams.web.client.ui.pages.activity.ActivityContent

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.