/*
* Copyright (c) 2010-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.common.stream.renderers;
import java.io.Serializable;
import java.util.HashMap;
import java.util.List;
import org.eurekastreams.server.action.request.stream.SetActivityLikeRequest.LikeActionType;
import org.eurekastreams.server.search.modelview.PersonModelView;
import org.eurekastreams.web.client.events.EventBus;
import org.eurekastreams.web.client.events.Observer;
import org.eurekastreams.web.client.events.data.ResourceLikeChangeEvent;
import org.eurekastreams.web.client.jsni.EffectsFacade;
import org.eurekastreams.web.client.jsni.WidgetJSNIFacadeImpl;
import org.eurekastreams.web.client.model.LikeResourceModel;
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.avatar.AvatarLinkPanel;
import org.eurekastreams.web.client.ui.common.avatar.AvatarWidget.Size;
import org.eurekastreams.web.client.ui.pages.master.StaticResourceBundle;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.MouseOverEvent;
import com.google.gwt.event.dom.client.MouseOverHandler;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Event;
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.FocusPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.RootPanel;
/**
* Like count widget.
*/
public class ResourceCountWidget extends Composite
{
/**
* How far down the popout arrow needs to be from the top of the bubble given the button it points to.
*/
private static final int POPOUT_ARROW_TOP_OFFSET = -6;
/**
* Main widget.
*/
private final FocusPanel widget = new FocusPanel();
/**
* Shows users who have liked this.
*/
private static FlowPanel usersWhoLikedPanel = new FlowPanel();
/**
* Shows users who have liked this.
*/
private static FlowPanel usersWhoLikedPanelWrapper;
/**
* Arrow pointing from the bubble to the button.
*/
private static Label popoutArrow = new Label();
/**
* Added to root panel.
*/
private static boolean hasUsersWhoLikedBeenAddedToRoot = false;
/**
* Like count.
*/
private Integer likeCount = 0;
/**
* Link for the like count link.
*/
final Anchor likeCountLink = new Anchor();
/**
* Avatar panel.
*/
private static FlowPanel avatarPanel = new FlowPanel();
/**
* Likers.
*/
private final List<PersonModelView> likers;
/**
* Liker overflow.
*/
private PersonModelView likerOverflow;
/**
* Liked status.
*/
private Boolean thisIsLiked;
/**
* The current panel.
*/
private static ResourceCountWidget currentPanel;
/**
* Link for the like count link.
*/
private static Anchor innerLikeCountLink = new Anchor();
/**
* The view all link.
*/
private static Anchor viewAll = new Anchor("view all");
/**
* The body.
*/
private static FlowPanel userLikedBody = new FlowPanel();
/**
* How many do we show before the view all link shows up.
*/
private static final int MAXLIKERSSHOWN = 4;
/**
* If the user has mouse over.
*/
private static int hasMousedOver = 0;
/**
* Resouce url.
*/
private static String resourceUrl;
/**
* Count type.
*/
private static CountType countType;
/**
* Count Type.
*/
public enum CountType
{
/**
* Like count.
*/
LIKES,
/**
* Share count.
*/
SHARES
}
/**
* Timer factory.
*/
private static TimerFactory timerFactory = new TimerFactory();
/**
* Timer expiration.
*/
private static final int TIMER_EXPIRATION = 250;
/**
* Timer expiration for intial show.
*/
private static final int INITIAL_TIMER_EXPIRATION = 2500;
/**
* Setup the floating avatar panel.
*/
private static void setup()
{
// Reimplementing Focus panel, GWT seems to break otherwise.
usersWhoLikedPanelWrapper = new FlowPanel()
{
private boolean actuallyOut = false;
@Override
public void onBrowserEvent(final Event event)
{
super.onBrowserEvent(event);
if (DOM.eventGetType(event) == Event.ONMOUSEOUT)
{
actuallyOut = true;
timerFactory.runTimer(TIMER_EXPIRATION, new TimerHandler()
{
public void run()
{
if (actuallyOut)
{
usersWhoLikedPanelWrapper.setVisible(false);
}
}
});
}
else if (DOM.eventGetType(event) == Event.ONMOUSEOVER)
{
actuallyOut = false;
hasMousedOver++;
}
else if (DOM.eventGetType(event) == Event.ONCLICK)
{
usersWhoLikedPanelWrapper.setVisible(false);
}
}
};
popoutArrow.addStyleName(StaticResourceBundle.INSTANCE.coreCss().eurekaConnectPopoutArrow());
usersWhoLikedPanelWrapper.add(popoutArrow);
viewAll.addStyleName(StaticResourceBundle.INSTANCE.coreCss().eurekaConnectShowAllUsersWhoLikedActivity());
viewAll.addStyleName(StaticResourceBundle.INSTANCE.coreCss().hide());
// viewAll.setVisible(false);
viewAll.addClickHandler(new ClickHandler()
{
public void onClick(final ClickEvent arg0)
{
showActorsPopup(countType, resourceUrl);
}
});
usersWhoLikedPanelWrapper.setVisible(false);
usersWhoLikedPanelWrapper.addStyleName(StaticResourceBundle.INSTANCE.coreCss().usersWhoLikedActivityWrapper());
usersWhoLikedPanelWrapper.addStyleName(StaticResourceBundle.INSTANCE.coreCss()
.eurekaConnectLikedActivityWrapper());
usersWhoLikedPanelWrapper.addStyleName(StaticResourceBundle.INSTANCE.coreCss().likeCountWidget());
RootPanel.get().add(usersWhoLikedPanelWrapper);
usersWhoLikedPanelWrapper.add(usersWhoLikedPanel);
usersWhoLikedPanel.addStyleName(StaticResourceBundle.INSTANCE.coreCss().usersWhoLikedActivity());
usersWhoLikedPanel.addStyleName(StaticResourceBundle.INSTANCE.coreCss().eurekaConnectUsersWhoLikedActivity());
usersWhoLikedPanel.addStyleName(StaticResourceBundle.INSTANCE.coreCss().eurekaConnectLikedActivity());
FlowPanel userLikedHeader = new FlowPanel();
userLikedHeader.addStyleName(StaticResourceBundle.INSTANCE.coreCss().usersWhoLikedActivityHeader());
usersWhoLikedPanel.add(userLikedHeader);
userLikedBody.addStyleName(StaticResourceBundle.INSTANCE.coreCss().usersWhoLikedActivityBody());
usersWhoLikedPanel.add(userLikedBody);
FlowPanel userLikedFooter = new FlowPanel();
userLikedFooter.addStyleName(StaticResourceBundle.INSTANCE.coreCss().usersWhoLikedActivityFooter());
usersWhoLikedPanel.add(userLikedFooter);
userLikedBody.add(avatarPanel);
userLikedBody.add(viewAll);
usersWhoLikedPanelWrapper.sinkEvents(Event.ONMOUSEOUT | Event.ONMOUSEOVER | Event.ONCLICK);
hasUsersWhoLikedBeenAddedToRoot = true;
}
/**
* Constructor.
*
* @param inCountType
* the count type.
* @param inResoureceUrl
* activity url.
* @param thumbs
* thumbnails.
* @param desc
* description.
* @param title
* title.
* @param inLikeCount
* like count.
* @param inLikers
* who has liked this activity.
* @param inIsLiked
* if the person has liked the current activity.
*/
public ResourceCountWidget(final CountType inCountType, final String inResoureceUrl, final String title,
final String desc, final String[] thumbs, final Integer inLikeCount, final List<PersonModelView> inLikers,
final Boolean inIsLiked)
{
thisIsLiked = inIsLiked;
countType = inCountType;
resourceUrl = inResoureceUrl;
initWidget(widget);
if (!hasUsersWhoLikedBeenAddedToRoot)
{
setup();
}
widget.addMouseOverHandler(new MouseOverHandler()
{
public void onMouseOver(final MouseOverEvent arg0)
{
countType = inCountType;
resourceUrl = inResoureceUrl;
if (likeCount > 0)
{
showPanel();
usersWhoLikedPanelWrapper.setVisible(true);
}
}
});
likers = inLikers;
likeCount = inLikeCount;
likeCountLink.addStyleName(StaticResourceBundle.INSTANCE.coreCss().eurekaConnectCount());
likeCountLink.addClickHandler(new ClickHandler()
{
public void onClick(final ClickEvent event)
{
showActorsPopup(inCountType, inResoureceUrl);
}
});
final FlowPanel likeCountPanel = new FlowPanel();
if (inCountType.equals(CountType.LIKES))
{
final FlowPanel likeContainer = new FlowPanel();
likeContainer.addStyleName(StaticResourceBundle.INSTANCE.coreCss().eurekaConnectLikeButton());
if (thisIsLiked)
{
likeContainer.addStyleName(StaticResourceBundle.INSTANCE.coreCss().eurekaConnectUnlikeButton());
}
else
{
likeContainer.addStyleName(StaticResourceBundle.INSTANCE.coreCss().eurekaConnectLikeButton());
}
Anchor likeLink = new Anchor();
EventBus.getInstance().addObserver(ResourceLikeChangeEvent.class, new Observer<ResourceLikeChangeEvent>()
{
public void update(final ResourceLikeChangeEvent event)
{
if (event.getResponse())
{
likeContainer.removeStyleName(StaticResourceBundle.INSTANCE.coreCss()
.eurekaConnectLikeButton());
likeContainer
.addStyleName(StaticResourceBundle.INSTANCE.coreCss().eurekaConnectUnlikeButton());
updatePanel(LikeActionType.ADD_LIKE);
currentPanel.showPanel();
usersWhoLikedPanelWrapper.setVisible(true);
}
else
{
likeContainer.removeStyleName(StaticResourceBundle.INSTANCE.coreCss()
.eurekaConnectUnlikeButton());
likeContainer.addStyleName(StaticResourceBundle.INSTANCE.coreCss().eurekaConnectLikeButton());
updatePanel(LikeActionType.REMOVE_LIKE);
currentPanel.showPanel();
usersWhoLikedPanelWrapper.setVisible(false);
}
likeCountLink.setText(likeCount.toString());
}
});
likeLink.addClickHandler(new ClickHandler()
{
public void onClick(final ClickEvent arg0)
{
thisIsLiked = !thisIsLiked;
final HashMap<String, Serializable> request = new HashMap<String, Serializable>();
request.put("resourceurl", inResoureceUrl);
request.put("liked", thisIsLiked);
LikeResourceModel.getInstance().insert(request);
}
});
likeContainer.add(likeLink);
likeContainer.add(likeCountLink);
likeCountPanel.add(likeContainer);
}
else
{
FlowPanel shareContainer = new FlowPanel();
shareContainer.addStyleName(StaticResourceBundle.INSTANCE.coreCss().eurekaConnectShareButton());
Anchor shareLink = new Anchor();
shareLink.addClickHandler(new ClickHandler()
{
public void onClick(final ClickEvent arg0)
{
String thumbsStr = "";
final int height = 380;
final int width = 800;
for (String thumb : thumbs)
{
if (thumbsStr.length() > 0)
{
thumbsStr += ",";
}
thumbsStr += thumb;
}
Window.open("/widget.html?widget=sharedialog&thumbs=" + thumbsStr + "&desc=" + desc + "&title="
+ title + "&resourceurl=" + inResoureceUrl, null,
WidgetJSNIFacadeImpl.nativeGetCenteredPopupFeatureString(width, height)
+ ",status=yes,toolbar=no,menubar=no,location=no");
}
});
shareContainer.add(shareLink);
shareContainer.add(likeCountLink);
likeCountPanel.add(shareContainer);
}
widget.add(likeCountPanel);
updatePanel(null);
}
/**
* Called to update the panel.
*/
private void showPanel()
{
hasMousedOver++;
final int hasMouseOverVal = hasMousedOver;
currentPanel = this;
viewAll.addStyleName(StaticResourceBundle.INSTANCE.coreCss().hide());
// viewAll.setVisible(false);
avatarPanel.clear();
int widgetTop = widget.getAbsoluteTop();
int newTopMargin = widgetTop + POPOUT_ARROW_TOP_OFFSET;
DOM.setStyleAttribute(popoutArrow.getElement(), "marginTop", newTopMargin + "px");
for (PersonModelView liker : likers)
{
avatarPanel.add(AvatarLinkPanel.create(liker, Size.VerySmall));
}
if (likeCount > MAXLIKERSSHOWN)
{
viewAll.removeStyleName(StaticResourceBundle.INSTANCE.coreCss().hide());
// viewAll.setVisible(true);
}
innerLikeCountLink.setText(likeCount.toString());
timerFactory.runTimer(INITIAL_TIMER_EXPIRATION, new TimerHandler()
{
public void run()
{
if (hasMousedOver == hasMouseOverVal)
{
usersWhoLikedPanelWrapper.setVisible(false);
}
}
});
}
/**
* Update the panel.
*
* @param likeActionType
* the action that's being taken.
*/
private void updatePanel(final LikeActionType likeActionType)
{
if (null != likeActionType)
{
if (likeActionType == LikeActionType.ADD_LIKE)
{
PersonModelView currentPerson = new PersonModelView();
currentPerson.setEntityId(Session.getInstance().getCurrentPerson().getId());
currentPerson.setAvatarId(Session.getInstance().getCurrentPerson().getAvatarId());
currentPerson.setDisplayName(Session.getInstance().getCurrentPerson().getDisplayName());
currentPerson.setAccountId(Session.getInstance().getCurrentPerson().getAccountId());
if (likers.size() >= MAXLIKERSSHOWN)
{
likerOverflow = likers.get(MAXLIKERSSHOWN - 1);
likers.remove(MAXLIKERSSHOWN - 1);
}
likers.add(0, currentPerson);
likeCount++;
if (!widget.isVisible())
{
EffectsFacade.nativeFadeIn(widget.getElement(), true);
}
}
else
{
for (PersonModelView liker : likers)
{
if (likeActionType == LikeActionType.REMOVE_LIKE
&& liker.getId() == Session.getInstance().getCurrentPerson().getId())
{
likers.remove(liker);
if (likerOverflow != null)
{
likers.add(9, likerOverflow);
}
}
}
likeCount--;
}
}
likeCountLink.setText(likeCount.toString());
}
/**
* Pops up a window showing a list of people.
*
* @param inCountType
* Type of people.
* @param inResoureceUrl
* URL of resource.
*/
private static void showActorsPopup(final CountType inCountType, final String inResoureceUrl)
{
final int height = 320;
final int width = 400;
String actorType = CountType.LIKES.equals(inCountType) ? "likes" : "shares";
Window.open("/widget.html?widget=actordialog&actortype=" + actorType + "&resourceurl=" + inResoureceUrl, null,
WidgetJSNIFacadeImpl.nativeGetCenteredPopupFeatureString(width, height)
+ ",status=yes,toolbar=no,menubar=no,location=no");
}
}