/** * */ package org.gcube.portlets.user.newsfeed.client.ui; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import org.gcube.common.portal.GCubePortalConstants; import org.gcube.portal.databook.client.GCubeSocialNetworking; import org.gcube.portal.databook.client.util.Encoder; import org.gcube.portal.databook.shared.Attachment; import org.gcube.portal.databook.shared.Comment; import org.gcube.portal.databook.shared.EnhancedFeed; import org.gcube.portal.databook.shared.Feed; import org.gcube.portal.databook.shared.UserInfo; import org.gcube.portlets.user.gcubewidgets.client.ClientScopeHelper; import org.gcube.portlets.user.newsfeed.client.event.AddLikeEvent; import org.gcube.portlets.user.newsfeed.client.event.DeleteFeedEvent; import org.gcube.portlets.user.newsfeed.client.event.OpenFeedEvent; import org.gcube.portlets.user.newsfeed.client.event.SeeCommentsEvent; import org.gcube.portlets.user.newsfeed.client.event.SeeLikesEvent; import org.gcube.portlets.user.newsfeed.client.event.UnLikeEvent; import org.gcube.portlets.user.newsfeed.client.panels.NewsFeedPanel; import org.gcube.portlets.widgets.imagepreviewerwidget.client.EnhancedImage; import org.gcube.portlets.widgets.imagepreviewerwidget.client.ui.Carousel; import com.google.gwt.core.client.GWT; import com.google.gwt.core.client.RunAsyncCallback; import com.google.gwt.event.dom.client.ClickEvent; import com.google.gwt.event.dom.client.ClickHandler; import com.google.gwt.event.dom.client.MouseOutEvent; import com.google.gwt.event.dom.client.MouseOverEvent; import com.google.gwt.event.shared.HandlerManager; import com.google.gwt.i18n.client.DateTimeFormat; import com.google.gwt.uibinder.client.UiBinder; import com.google.gwt.uibinder.client.UiField; import com.google.gwt.uibinder.client.UiHandler; import com.google.gwt.user.client.Timer; import com.google.gwt.user.client.Window; import com.google.gwt.user.client.Window.Location; import com.google.gwt.user.client.ui.Composite; import com.google.gwt.user.client.ui.HTML; import com.google.gwt.user.client.ui.HTMLPanel; import com.google.gwt.user.client.ui.Image; import com.google.gwt.user.client.ui.Label; import com.google.gwt.user.client.ui.VerticalPanel; import com.google.gwt.user.client.ui.Widget; /** * @author Massimiliano Assante at ISTI-CNR * @author Costantino Perciante at ISTI-CNR * */ public class TweetTemplate extends Composite { private static TweetTemplateUiBinder uiBinder = GWT .create(TweetTemplateUiBinder.class); interface TweetTemplateUiBinder extends UiBinder { } public static final String loading = GWT.getModuleBaseURL() + "../images/loading-comments.gif"; private static final int MAX_SHOWTEXT_LENGTH = 256; private EnhancedFeed myFeed; private UserInfo myUserInfo; private HandlerManager eventBus; private ArrayList myComments; private boolean commentingDisabled = false; private boolean commentsFetched = false; private int totalComments = 0; private HTML showAllComments = new HTML(); private boolean isAppFeed = false; private TweetTemplate myInstance; // Carousel from the image-previewer widget private Carousel carousel; /** * tell if this tweet is belonging to the current user */ private boolean isUsers = false; @UiField HTML contentArea; @UiField HTML seeMore; @UiField HTML timeArea; @UiField HTML likeArea; @UiField HTML commentArea; @UiField HTML sharePostArea; @UiField Image avatarImage; @UiField AvatarReplacement avatarReplacement; @UiField HTMLPanel mainHTML; @UiField HTML likesNo; @UiField HTML commentsNo; @UiField VerticalPanel commentsPanel; @UiField HTML closeImage; @UiField HTML openImage; @UiField HTML vreSource; @UiField VerticalPanel previewPanel; @UiField Placeholder attachmentPreviewPanel; @UiField Label messageSeparator; /** * used when fetching tweets from server * @param myUserInfo * @param myFeed * @param isUsers * @param displaySingle tells if you're displaying a single fedd or not * @param eventBus */ public TweetTemplate(boolean displaySingle, boolean showTimelineSource, UserInfo myUserInfo, EnhancedFeed myFeed, HandlerManager eventBus) { initWidget(uiBinder.createAndBindUi(this)); myInstance = this; this.myUserInfo = myUserInfo; this.vreSource.setVisible(false); this.myFeed = myFeed; isAppFeed = myFeed.getFeed().isApplicationFeed(); Feed feed = myFeed.getFeed(); this.eventBus = eventBus; this.isUsers = myFeed.isUsers(); this.carousel = new Carousel(); myComments = new ArrayList(); if (isUsers || myUserInfo.isAdmin()) { closeImage.setStyleName("closeImage"); closeImage.setTitle(myUserInfo.isAdmin() ? "Delete (Administrator Mode)" : "delete"); } else { closeImage.removeFromParent(); } // if there is one attachment or a link preview, maintain backward compatibility if (feed.getUri() != null && feed.getUri().compareTo("") != 0 && feed.getLinkTitle() != null && feed.getLinkTitle().compareTo("") != 0 && !feed.isMultiFileUpload()) { // hide the attachments panel attachmentPreviewPanel.setVisible(false); LinkPreviewer linkPreviewer = new LinkPreviewer(feed.getLinkTitle(), feed.getLinkDescription(), feed.getLinkHost(), feed.getUriThumbnail(), feed.getUri()); // enable the image previewer if it is an image (mime) if(feed.getLinkHost().contains("image/")){ ArrayList listOfEnhancedImages; EnhancedImage enhancedImage = new EnhancedImage( new com.github.gwtbootstrap.client.ui.Image( feed.getUri()), feed.getLinkTitle() + " (" + feed.getLinkDescription() + ", type:" + feed.getLinkHost() +")", feed.getLinkTitle(), feed.getUri() ); listOfEnhancedImages = new ArrayList(); listOfEnhancedImages.add(enhancedImage); carousel.updateImages(listOfEnhancedImages); // set handler on the linkpreviewer image to show this carousel and on the image title too linkPreviewer.onImageClickOpenCarousel(carousel); linkPreviewer.onFileNameClickOpenCarousel(carousel); // remove next and prev buttons of the carousel since we have only an image carousel.hideArrows(); } // add link preview to the preview panel previewPanel.add(linkPreviewer); } // in case there are attachments, we have to fill attachmentPreviewPanel instead of the previewPanel if(feed.isMultiFileUpload()){ // set style to the attachment container attachmentPreviewPanel.setStyleName("attachment-preview-container"); // hide link preview panel previewPanel.setVisible(false); // prepare the carousel ArrayList listOfEnhancedImages = new ArrayList(); // remember that one attachment is stored in the fields: uri, uriThumbnail, linkTitle, linkDescription, linkHost Attachment firstAttachment = new Attachment( feed.getKey(), // it is meaningless but it's needed feed.getUri(), feed.getLinkTitle(), feed.getLinkDescription(), feed.getUriThumbnail(), feed.getLinkHost()); // create first attachment previewer and pass it the carousel AttachmentPreviewer firstAttachmentPreviewer = new AttachmentPreviewer(firstAttachment); // determine if the left/right arrows must be removed int imagesAvailableInCarousel = 0; // check if it is an image if(firstAttachment.getMimeType().contains("image/")){ EnhancedImage enhancedImage = new EnhancedImage( new com.github.gwtbootstrap.client.ui.Image( feed.getUri()), feed.getLinkTitle() + " (" + feed.getLinkDescription() + ", type:" + feed.getLinkHost() +")", feed.getLinkTitle(), feed.getUri() ); listOfEnhancedImages.add(enhancedImage); firstAttachmentPreviewer.onImageClickOpenCarousel(carousel, enhancedImage); // increment the images imagesAvailableInCarousel ++; } // add the first attachment to the panel attachmentPreviewPanel.add(firstAttachmentPreviewer); // check the others for (Attachment otherAttachment : myFeed.getAttachments()) { AttachmentPreviewer attachmentPreviewer = new AttachmentPreviewer(otherAttachment); if(otherAttachment.getMimeType().contains("image/")){ EnhancedImage enhancedImage = new EnhancedImage( new com.github.gwtbootstrap.client.ui.Image( otherAttachment.getUri()), otherAttachment.getName() + " (" + otherAttachment.getDescription() + ", type:" + feed.getLinkHost() +")", otherAttachment.getName(), otherAttachment.getUri() ); listOfEnhancedImages.add(enhancedImage); // pass the carousel attachmentPreviewer.onImageClickOpenCarousel(carousel, enhancedImage); // increment the images imagesAvailableInCarousel ++; } // try to build the attachment viewer attachmentPreviewPanel.add(attachmentPreviewer); // hide arrows if there is no more than 1 image if(imagesAvailableInCarousel <= 1) carousel.hideArrows(); } // update the carousel's images carousel.updateImages(listOfEnhancedImages); // invoke append label attachmentPreviewPanel.appendShowMoreLabel(); } openImage.setStyleName("openImage"); openImage.setTitle("Open this feed separately"); //show if the user has already liked this or not setFavoritedUI(myFeed.isLiked()); commentArea.setHTML("" + NewsFeedPanel.COMMENT_LABEL + ""); String feedText = feed.getDescription(); String descWithoutHTML = new HTML(feedText).getText(); if ( (! feedText.startsWith(" MAX_SHOWTEXT_LENGTH && !displaySingle) { final int TEXT_TO_SHOW_LENGHT = (descWithoutHTML.length() < 500) ? (feedText.length() - (feedText.length() / 3)) : 500; feedText = feedText.substring(0, TEXT_TO_SHOW_LENGHT) + "..."; seeMore.setHTML(" See More "); } avatarImage.setUrl(feed.getThumbnailURL()); avatarImage.setPixelSize(50, 50); //replace the < & and > feedText = feedText.replaceAll("<","<").replaceAll(">",">"); feedText = feedText.replaceAll("&","&"); final String profilePageURL = GCubePortalConstants.PREFIX_GROUP_URL + ClientScopeHelper.extractOrgFriendlyURL(Location.getHref()) +GCubePortalConstants.USER_PROFILE_FRIENDLY_URL; if (! isAppFeed) { // sharePostArea.setHTML("" + NewsFeedPanel.SHARE_FWD_LABEL + ""); contentArea.setHTML(""+feed.getFullName()+" " + feedText); this.vreSource.setVisible(true); //show the vreid iff the info is present if (showTimelineSource && feed.getVreid() != null && feed.getVreid().compareTo("") != 0) { String vreName = feed.getVreid().substring(feed.getVreid().lastIndexOf("/")+1); vreSource.setHTML("[" +vreName + "]"); } //check if the user has his own avatar if (feed.getThumbnailURL().endsWith("img_id=0") || !feed.getThumbnailURL().contains("?")) { //it means no avatar is set avatarImage.setVisible(false); String f = "A"; String s = "Z"; if (feed.getFullName() != null) { String[] parts = feed.getFullName().split("\\s"); if (parts.length > 0) { f = parts[0].toUpperCase(); s = parts[parts.length-1].toUpperCase(); } else { f = feed.getFullName().substring(0,1); s = feed.getFullName().substring(1,2); } } avatarReplacement.setInitials(feed.getEntityId(), f, s); avatarReplacement.setVisible(true); } } else { // messageSeparator.setVisible(false); contentArea.setHTML(""+feed.getFullName()+" " + feedText); if (isAppFeed) { if (myUserInfo.isAdmin()) closeImage.setTitle("Delete this Application feed (Administrator Only)"); else closeImage.removeFromParent(); try{ String vreName = feed.getVreid().substring(feed.getVreid().lastIndexOf("/")+1); sharePostArea.setHTML(" - go App [" +vreName + "] - "); } catch (Exception e) {} } } try { String formattedTime = DateTimeFormat.getFormat("MMMM dd, h:mm a").format(feed.getTime()); timeArea.setHTML(formattedTime); String formattedTimeWithYear = DateTimeFormat.getFormat("dd MMMM yyyy h:mm a ").format(feed.getTime()); timeArea.setTitle(formattedTimeWithYear); if (! feed.getCommentsNo().equals("0")) { commentsNo.setHTML(feed.getCommentsNo()); commentsNo.setStyleName("comments-number"); commentsNo.setTitle("People commented this."); } if (! feed.getLikesNo().equals("0")) { likesNo.setHTML(feed.getLikesNo()); likesNo.setStyleName("likes-number"); likesNo.setTitle("Show People who have " + NewsFeedPanel.LIKED_LABEL + " this."); } totalComments = Integer.parseInt(feed.getCommentsNo()); } catch (NumberFormatException e) { totalComments = 0; } catch (Exception e) { timeArea.setHTML("just now"); } commentsPanel.setStyleName("commentsPanel"); if (myFeed.getComments() != null && myFeed.getComments().size() > 0) { if (totalComments > 2 && !displaySingle) { showAllComments = getShowAllCommentsLink(totalComments); commentsPanel.add(showAllComments); commentsNo.setStyleName("show-comments-number"); commentsNo.setTitle("Show all Comments"); } for (Comment comment : myFeed.getComments()) { addComment(new SingleComment(comment, this, (comment.getUserid().equals(myUserInfo.getUsername())))); } showAddCommentForm(false); } } /** * used when getting tweets from the client * @param myUserInfo * @param feed * @param eventBus * @param hidden */ public TweetTemplate(UserInfo myUserInfo, EnhancedFeed feed, HandlerManager eventBus, boolean hidden) { this(false, false, myUserInfo, feed, eventBus); contentArea.getElement().getParentElement().getParentElement().setClassName("div-table-col content hidden"); } @UiHandler("contentArea") public void onHover(MouseOutEvent event) { if (isUsers) closeImage.removeStyleName("uiCloseButton"); openImage.removeStyleName("uiOpenButton"); } @UiHandler("contentArea") public void onHover(MouseOverEvent event) { if (isUsers) { closeImage.addStyleName("uiCloseButton"); GWT.log("this belong to user"); } openImage.addStyleName("uiOpenButton"); } @UiHandler("closeImage") void onDeleteFeedClick(ClickEvent e) { if (isUsers || myUserInfo.isAdmin()){ eventBus.fireEvent(new DeleteFeedEvent(this)); } else { GWT.log("not belong to user"); } } @UiHandler("openImage") void onOpenFeedClick(ClickEvent e) { eventBus.fireEvent(new OpenFeedEvent(this)); } @UiHandler("seeMore") void onSeeMoreClick(ClickEvent e) { String feedText = myFeed.getFeed().getDescription(); //replace the < & and > feedText = feedText.replaceAll("<","<").replaceAll(">",">"); feedText = feedText.replaceAll("&","&"); final String profilePageURL = GCubePortalConstants.PREFIX_GROUP_URL + ClientScopeHelper.extractOrgFriendlyURL(Location.getHref()) +GCubePortalConstants.USER_PROFILE_FRIENDLY_URL; contentArea.setHTML(""+ myFeed.getFeed().getFullName()+" " + feedText); seeMore.setHTML(""); } private void setFavoritedUI(boolean favorited) { if (favorited) { likeArea.setHTML("" + NewsFeedPanel.LIKED_LABEL + ""); likeArea.setTitle("Unfavorite this"); } else { likeArea.setHTML("" + NewsFeedPanel.LIKE_LABEL + ""); } } @UiHandler("likeArea") void onLikeClick(ClickEvent e) { //if is not liked if (!likeArea.getText().equals(NewsFeedPanel.LIKED_LABEL)) { try { int cur = Integer.parseInt(myFeed.getFeed().getLikesNo()); cur++; if (cur == 1) { myFeed.getFeed().setLikesNo("1"); likesNo.setHTML("1"); likesNo.setStyleName("likes-number"); likesNo.setTitle("People who have " + NewsFeedPanel.LIKED_LABEL + " this"); } else { myFeed.getFeed().setLikesNo(""+cur); likesNo.setHTML(""+cur); } eventBus.fireEvent(new AddLikeEvent(this, myFeed.getFeed().getKey())); setFavoritedUI(true); } catch (NumberFormatException ex) { likeArea.setHTML("Error on the server"); } } else { //it is liked int cur = Integer.parseInt(myFeed.getFeed().getLikesNo()); cur--; if (cur == 0) { myFeed.getFeed().setLikesNo("0"); likesNo.setHTML(""); likesNo.removeStyleName("likes-number"); likesNo.setTitle(""); } else { myFeed.getFeed().setLikesNo(""+cur); likesNo.setHTML(""+cur); } eventBus.fireEvent(new UnLikeEvent(this, myFeed.getFeed().getKey())); setFavoritedUI(false); } } @UiHandler("commentArea") void onAddCommentClick(ClickEvent e) { if (! commentingDisabled) { if (! commentsFetched && totalComments > 2) { //if so, need to load all comments before adding a comment fireSeeComments(true); } else { showAddCommentForm(true); } } else GWT.log("Commenting disabled"); } @UiHandler("sharePostArea") void onMessageClick(ClickEvent e) { if (! isAppFeed) { final List listToLogin = new ArrayList(); listToLogin.add(myFeed.getFeed().getEntityId()); GWT.runAsync(new RunAsyncCallback() { @Override public void onSuccess() { SharePostDialog dlg = new SharePostDialog(myInstance); dlg.openModal(); } public void onFailure(Throwable reason) { Window.alert("Could not load this component: " + reason.getMessage()); } }); } } public void showAddCommentForm(boolean focus) { final AddCommentTemplate toAdd = new AddCommentTemplate(this, myUserInfo, eventBus); commentsPanel.add(toAdd); commentingDisabled = true; final Timer t = new Timer() { @Override public void run() { toAdd.setStyleName("comment-show"); } }; if (focus) toAdd.setFocus(); t.schedule(10); } private HTML getShowAllCommentsLink(int commentsNo) { final HTML toReturn = new HTML(""); toReturn.addClickHandler(new ClickHandler() { @Override public void onClick(ClickEvent event) { fireSeeComments(false); } }); return toReturn; } private void fireSeeComments(boolean commentForm2Add) { eventBus.fireEvent(new SeeCommentsEvent(this, commentForm2Add)); } @UiHandler("likesNo") void onSeeLikes(ClickEvent e) { eventBus.fireEvent(new SeeLikesEvent(myFeed.getFeed().getKey())); } @UiHandler("commentsNo") void onSeeComments(ClickEvent e) { fireSeeComments(false); } public void setcontentAreaStyle(String cssclass) { contentArea.getElement().getParentElement().getParentElement().setClassName("div-table-col content visible"); } public boolean isCommenting() { return commentingDisabled; } public void setCommentingDisabled(boolean commenting) { this.commentingDisabled = commenting; } public String getFeedKey() { return myFeed.getFeed().getKey(); } public void remove(Widget w) { mainHTML.remove(w); } public void addComment(SingleComment comment) { commentsPanel.add(comment); myComments.add(comment); } public void updateSingleComment(Comment edited, HTMLPanel commentPanel){ commentPanel.clear(); SingleComment sc = new SingleComment(edited, this, true); commentPanel.add(sc); // replace the new SingleComment in the list int index = 0; Iterator iterator = this.myComments.iterator(); for (;iterator.hasNext();) { SingleComment singleComment = (SingleComment) iterator.next(); if(singleComment.getCommentKey().equals(edited.getKey())){ iterator.remove(); this.myComments.add(index, sc); break; } index ++; } } public void clearComments() { myComments.clear(); commentsPanel.clear(); } public void showLoadingComments() { showAllComments.setHTML("
"); } public boolean isCommentsFetched() { return commentsFetched; } public void setCommentsFetched(boolean commentsFetched) { this.commentsFetched = commentsFetched; } public HandlerManager getEventBus() { return eventBus; } public void updateCommentsNumberCount() { if (myComments.size() == 1) { commentsNo.setStyleName("comments-number"); commentsNo.setTitle("Persons who have commented this."); } commentsNo.setHTML(""+myComments.size()); } public UserInfo getMyUserInfo() { return myUserInfo; } public String getMyFeedUserId() { return myFeed.getFeed().getEntityId(); } public String getMyFeedText() { return myFeed.getFeed().getDescription(); } public boolean isAppFeed() { return isAppFeed; } public boolean isUser() { return isUsers; } /** * Returns the number of comments this post has * @return */ public int numberOfComments(){ // quite easy.. return myComments.size(); } /** * Returns the number of likes this post has * @return */ public int numberOfLikes(){ // not so easy int ret = 0; try{ ret = Integer.parseInt(likesNo.getHTML()); }catch(NumberFormatException e){ GWT.log(e.toString()); } return ret; } }