/* * 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"); } }