package org.gcube.portlets.user.newsfeed.client.panels; import java.util.ArrayList; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.HashSet; 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.ClientAttachment; import org.gcube.portal.databook.shared.ClientPost; 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.FeedType; import org.gcube.portal.databook.shared.Like; import org.gcube.portal.databook.shared.PrivacyLevel; import org.gcube.portal.databook.shared.ShowUserStatisticAction; import org.gcube.portal.databook.shared.UserInfo; import org.gcube.portlets.user.newsfeed.client.FilterType; import org.gcube.portlets.user.newsfeed.client.NewsService; import org.gcube.portlets.user.newsfeed.client.NewsServiceAsync; import org.gcube.portlets.user.newsfeed.client.event.AddCommentEvent; import org.gcube.portlets.user.newsfeed.client.event.AddCommentEventHandler; import org.gcube.portlets.user.newsfeed.client.event.AddLikeEvent; import org.gcube.portlets.user.newsfeed.client.event.AddLikeEventHandler; import org.gcube.portlets.user.newsfeed.client.event.DeleteCommentEvent; import org.gcube.portlets.user.newsfeed.client.event.DeleteCommentEventHandler; import org.gcube.portlets.user.newsfeed.client.event.DeletePostEvent; import org.gcube.portlets.user.newsfeed.client.event.DeletePostEventHandler; import org.gcube.portlets.user.newsfeed.client.event.EditCommentEvent; import org.gcube.portlets.user.newsfeed.client.event.EditCommentEventHandler; import org.gcube.portlets.user.newsfeed.client.event.OpenPostEvent; import org.gcube.portlets.user.newsfeed.client.event.OpenPostEventHandler; import org.gcube.portlets.user.newsfeed.client.event.SeeCommentsEvent; import org.gcube.portlets.user.newsfeed.client.event.SeeCommentsEventHandler; import org.gcube.portlets.user.newsfeed.client.event.SeeLikesEvent; import org.gcube.portlets.user.newsfeed.client.event.SeeLikesEventHandler; import org.gcube.portlets.user.newsfeed.client.event.ShowMoreUpdatesEvent; import org.gcube.portlets.user.newsfeed.client.event.ShowMoreUpdatesEventHandler; import org.gcube.portlets.user.newsfeed.client.event.ShowNewUpdatesEvent; import org.gcube.portlets.user.newsfeed.client.event.ShowNewUpdatesEventHandler; import org.gcube.portlets.user.newsfeed.client.event.UnLikeEvent; import org.gcube.portlets.user.newsfeed.client.event.UnLikeEventHandler; import org.gcube.portlets.user.newsfeed.client.ui.FilterPanel; import org.gcube.portlets.user.newsfeed.client.ui.LoadingText; import org.gcube.portlets.user.newsfeed.client.ui.NewFeedsAvailable; import org.gcube.portlets.user.newsfeed.client.ui.ResultsFor; import org.gcube.portlets.user.newsfeed.client.ui.ShowMoreFeeds; import org.gcube.portlets.user.newsfeed.client.ui.SingleComment; import org.gcube.portlets.user.newsfeed.client.ui.TweetTemplate; import org.gcube.portlets.user.newsfeed.shared.MentionedDTO; import org.gcube.portlets.user.newsfeed.shared.MorePostsBean; import org.gcube.portlets.user.newsfeed.shared.NewsConstants; import org.gcube.portlets.user.newsfeed.shared.OperationResult; import org.gcube.portlets.user.newsfeed.shared.UserSettings; import org.gcube.portlets.widgets.userselection.client.UserSelectionDialog; import org.gcube.portlets.widgets.userselection.client.events.SelectedUserEvent; import org.gcube.portlets.widgets.userselection.client.events.SelectedUserEventHandler; import org.gcube.portlets.widgets.userselection.client.events.UsersFetchedEvent; import org.gcube.portlets.widgets.userselection.shared.ItemSelectableBean; import com.github.gwtbootstrap.client.ui.Row; import com.google.gwt.core.client.GWT; import com.google.gwt.dom.client.Document; import com.google.gwt.dom.client.Style.Unit; import com.google.gwt.event.shared.HandlerManager; 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.Window.ScrollEvent; import com.google.gwt.user.client.Window.ScrollHandler; import com.google.gwt.user.client.rpc.AsyncCallback; 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.HasAlignment; import com.google.gwt.user.client.ui.HasVerticalAlignment; import com.google.gwt.user.client.ui.HorizontalPanel; import com.google.gwt.user.client.ui.Image; import com.google.gwt.user.client.ui.SimplePanel; import com.google.gwt.user.client.ui.VerticalPanel; import com.google.gwt.user.client.ui.Widget; /** * * @author Massimiliano Assante, ISTI-CNR * */ public class NewsFeedPanel extends Composite { /** * the current scope on the client can be static */ private static String currentScope; /** * Create a remote service proxy to talk to the server-side News service. */ private final NewsServiceAsync newsService = GWT.create(NewsService.class); private final HandlerManager eventBus = new HandlerManager(null); private VerticalPanel mainPanel = new VerticalPanel(); private HorizontalPanel filterPanelWrapper = new HorizontalPanel(); private FilterPanel filterPanel; private SimplePanel newUpdatesPanel = new SimplePanel(); private Row showMoreUpdatesPanel = new Row(); private VerticalPanel newsPanel = new VerticalPanel(); private NewFeedsAvailable newsFeedAlert; private static final String warning = GWT.getModuleBaseURL() + "../images/warning_blue.png"; private static final String spacer = GWT.getModuleBaseURL() + "../images/feeds-spacer.gif"; public static final String loading = GWT.getModuleBaseURL() + "../images/feeds-loader.gif"; public static final String GET_OID_PARAMETER = "oid"; public static final String LIKE_LABEL = "Like"; public static final String LIKED_LABEL = "Liked"; public static final String COMMENT_LABEL = "Reply"; public static final String SHARE_FWD_LABEL = "Share"; private static final int SEARCHED_FEEDS_TO_SHOW = 10; private static final int FEEDS_RELATED_TO_USER_STATISTICS_TO_SHOW = 10; private static final String LIMIT_REACHED_FOR_SORTING_COMMENTS_TEXT = "You reached the limit, sort by newest comments works up to 100 posts."; private String vreLabel; private boolean showFeedTimelineSource = false; private boolean isInfrastructure = false; private int delayMillis = 300000; //5 minutes by default (is read from a configuration file in the first async callback) private int currNewUpdatesNo = 0; private boolean isFirstTweet = false; // is user searching for something? private boolean isSearch = false; // the current query (if isSearch is true) protected String currentQuery; // is the user seeing her statistics? private boolean isFeedsRelatedToStatistics = false; // the feeds related to user statistics to show protected ShowUserStatisticAction relatedFeedsToUserStatisticsToShow; private LoadingText loadingIcon = new LoadingText(); private Image loadingImage; private UserInfo myUserInfo; private FilterType currentFilter; private Timer feedsTimer; private ShowMoreFeeds showMoreWidget; //needed to know the next range start private Integer fromStartingPoint; private ArrayList allUpdates = new ArrayList(); private ArrayList tempCacheNewUpdates = new ArrayList(); /** * events binder */ private void bind() { eventBus.addHandler(ShowMoreUpdatesEvent.TYPE, new ShowMoreUpdatesEventHandler() { @Override public void onShowMoreUpdatesClick(ShowMoreUpdatesEvent event) { doShowMoreUpdates(); } }); eventBus.addHandler(ShowNewUpdatesEvent.TYPE, new ShowNewUpdatesEventHandler() { @Override public void onShowNewUpdatesClick(ShowNewUpdatesEvent event) { doShowCachedNewUpdates(); } }); eventBus.addHandler(AddLikeEvent.TYPE, new AddLikeEventHandler() { @Override public void onAddLike(AddLikeEvent event) { doAddLike( event.getOwner(), event.getFeedId()); } }); eventBus.addHandler(UnLikeEvent.TYPE, new UnLikeEventHandler() { @Override public void onUnLike(UnLikeEvent event) { doUnLike(event.getOwner(), event.getFeedId()); } }); eventBus.addHandler(AddCommentEvent.TYPE, new AddCommentEventHandler() { @Override public void onAddComment(AddCommentEvent event) { doAddComment(event.getOwner(), event.getText(), event.getMentionedUsers()); } }); eventBus.addHandler(EditCommentEvent.TYPE, new EditCommentEventHandler() { @Override public void onEditComment(EditCommentEvent event) { doEditComment(event.getOwner(), event.getCommentInstance(), event.getCommentPanel()); } }); eventBus.addHandler(SelectedUserEvent.TYPE, new SelectedUserEventHandler() { @Override public void onSelectedUser(SelectedUserEvent event) { final String profilePageURL = GCubePortalConstants.PREFIX_GROUP_URL + extractOrgFriendlyURL(Location.getHref()) +GCubePortalConstants.USER_PROFILE_FRIENDLY_URL; Location.assign(profilePageURL+"?"+ Encoder.encode(GCubeSocialNetworking.USER_PROFILE_OID)+"="+ Encoder.encode(event.getSelectedUser().getId())); } }); eventBus.addHandler(SeeLikesEvent.TYPE, new SeeLikesEventHandler() { @Override public void onSeeLikes(SeeLikesEvent event) { doShowLikes(event.getFeedId()); } }); eventBus.addHandler(SeeCommentsEvent.TYPE, new SeeCommentsEventHandler() { @Override public void onSeeComments(SeeCommentsEvent event) { doShowComments(event.getOwner(), event.isCommentForm2Add()); } }); eventBus.addHandler(DeleteCommentEvent.TYPE, new DeleteCommentEventHandler() { @Override public void onDeleteComment(DeleteCommentEvent event) { doDeleteComment(event.getOwner(), event.getCommentId()); } }); eventBus.addHandler(DeletePostEvent.TYPE, new DeletePostEventHandler() { @Override public void onDeletePost(DeletePostEvent event) { doDeletePost(event.getToDelete()); } }); eventBus.addHandler(OpenPostEvent.TYPE, new OpenPostEventHandler() { @Override public void onOpenPost(OpenPostEvent event) { doShowFeed(event.getToShow()); } }); } /** * */ public NewsFeedPanel() { bind(); mainPanel.setWidth("100%"); mainPanel.add(filterPanelWrapper); filterPanelWrapper.setVisible(false); mainPanel.add(newUpdatesPanel); mainPanel.add(newsPanel); filterPanel = new FilterPanel(this, newsService); filterPanelWrapper.add(filterPanel); initWidget(mainPanel); newsPanel.clear(); newsPanel.setWidth("100%"); showMoreUpdatesPanel.setWidth("60%"); newsPanel.setHeight("300px"); newsPanel.setHorizontalAlignment(HasAlignment.ALIGN_CENTER); newsPanel.setVerticalAlignment(HasVerticalAlignment.ALIGN_TOP); loadingImage = new Image(loading); newsPanel.add(loadingIcon); //CheckSession.getInstance().startPolling(); isSearch = false; isFeedsRelatedToStatistics = false; newsService.getUserSettings(new AsyncCallback() { @Override public void onFailure(Throwable caught) { } @Override public void onSuccess(UserSettings result) { myUserInfo = result.getUserInfo(); delayMillis = result.getRefreshingTimeInMillis(); vreLabel = result.getVreLabel(); currentScope = result.getCurrentScope(); if (result.getUserInfo().getUsername().equals(NewsConstants.TEST_USER)) { doStopFeedsTimer(); doShowSessionExpired(); } else { GWT.log("checking params "); if (getFeedToShowId() != null) { String feedKey = getFeedToShowId(); showSinglePost(feedKey); filterPanel.removeFilterSelected(); } else if (getHashtagParam() != null) { String hashtag = ""; try { hashtag = Encoder.decode(getHashtagParam()); } catch (Exception e) { newsPanel.clear(); newsPanel.add(new HTML("
" + "We're sorry, it seems you used an invalid character, please check the hashtag
")); return; } showFeedsByHashtag(hashtag); filterPanel.removeFilterSelected(); } else if (getSearchParam() != null) { String query = ""; try { query = Encoder.decode(getSearchParam()); currentQuery = query; } catch (Exception e) { newsPanel.clear(); newsPanel.add(new HTML("
" + "We're sorry, it seems you used an invalid character, please check the query
")); return; } // show isSearch = true; showFeedsSearch(query, 0, SEARCHED_FEEDS_TO_SHOW); filterPanel.removeFilterSelected(); }else if(getShowUserStatisticParam() != null){ String action = ""; try { action = Encoder.decode(getShowUserStatisticParam()); relatedFeedsToUserStatisticsToShow = ShowUserStatisticAction.valueOf(action); } catch (Exception e) { newsPanel.clear(); newsPanel.add(new HTML("
" + "We're sorry, it seems you used an invalid character
")); return; } isFeedsRelatedToStatistics = true; showUserStatisticsRelatedFeeds(relatedFeedsToUserStatisticsToShow, 0, FEEDS_RELATED_TO_USER_STATISTICS_TO_SHOW); filterPanel.removeFilterSelected(); } else { if (!result.isInfrastructure()) filterPanelWrapper.setVisible(getFeedToShowId() == null); showAllUpdatesFeeds(); } currentFilter = FilterType.ALL_UPDATES; } //adjustments in the UI Depending on the scope if (result.isInfrastructure()) { //filterPanelWrapper.setVisible(getFeedToShowId() == null); showFeedTimelineSource = result.isShowTimelineSourceLabel(); isInfrastructure = true; filterPanelWrapper.setVisible(getFeedToShowId() == null); } } }); feedsTimer = new Timer() { @Override public void run() { checkForNewUpdates(); } }; feedsTimer.scheduleRepeating(delayMillis); //this is for the automatic scroll of feeds Window.addWindowScrollHandler(new ScrollHandler() { @Override public void onWindowScroll(ScrollEvent event) { if (showMoreWidget != null) { boolean isInView = isScrolledIntoView(showMoreWidget); if (isInView) { eventBus.fireEvent(new ShowMoreUpdatesEvent()); } } } }); } /** * stop the feeds timer (when session expires) */ private void doStopFeedsTimer() { feedsTimer.cancel(); } /** * */ private void checkForNewUpdates() { switch (currentFilter) { case ALL_UPDATES: checkAllUpdatesFeeds(); break; case CONNECTIONS: showOnlyConnectionsFeeds(); break; case MINE: showOnlyMyPosts(); break; case RECENT_COMMENTS: checkAllUpdatesFeeds(); break; default: break; } } /** * check if it has to show just one feed * @return */ private String getFeedToShowId() { return Window.Location.getParameter(GET_OID_PARAMETER); } /** * check if it has to show the feeds given an hashtag * @return */ private String getHashtagParam() { return Window.Location.getParameter(Encoder.encode(GCubeSocialNetworking.HASHTAG_OID)); } /** * Check if some user statistic related feeds must be shown * @return */ private String getShowUserStatisticParam(){ return Window.Location.getParameter(Encoder.encode(GCubeSocialNetworking.SHOW_STATISTICS_ACTION_OID)); } /** * check if it has to show the feeds given a query * @return */ private String getSearchParam() { return Window.Location.getParameter(Encoder.encode(GCubeSocialNetworking.SEARCH_OID)); } /** * used from notification referrals (see this Post) * @param postKey */ private void showSinglePost(String postKey) { newsPanel.clear(); newsService.getSinglePost(postKey, new AsyncCallback() { @Override public void onSuccess(EnhancedFeed result) { if (result.getFeed().getType() == FeedType.DISABLED) { final String profilePageURL = GCubePortalConstants.PREFIX_GROUP_URL + extractOrgFriendlyURL(Location.getHref()) +GCubePortalConstants.USER_PROFILE_FRIENDLY_URL; String usrLink = ""+result.getFeed().getFullName()+ " "; newsPanel.add(new HTML("
" + "We're sorry, "+ usrLink +" removed the post in the meantime!
")); } else { mainPanel.setHorizontalAlignment(HasAlignment.ALIGN_LEFT); newsPanel.setHorizontalAlignment(HasAlignment.ALIGN_LEFT); newsPanel.setVerticalAlignment(HasVerticalAlignment.ALIGN_TOP); newsPanel.add(new ResultsFor("selected post", "")); newsPanel.setHeight(""); newsPanel.add(new TweetTemplate(true, true, myUserInfo, result, eventBus)); } newsPanel.add(new Image(spacer)); } @Override public void onFailure(Throwable caught) { showProblems(); } }); } /** * check for updates */ private void checkAllUpdatesFeeds() { newsService.getUserSettings(new AsyncCallback() { @Override public void onFailure(Throwable caught) { doStopFeedsTimer(); } @Override public void onSuccess(UserSettings result) { if (result.getUserInfo().getUsername().equals(NewsConstants.TEST_USER)) { doStopFeedsTimer(); doShowSessionExpired(); } /** * this check avoids the 2 tabs open in 2 different scope, if the previous tab was open at VRE Level and then antoher * is open at infra level the first tab stops checking for updates */ if (result.getCurrentScope().compareTo(currentScope) == 0) { newsService.getAllUpdateUserFeeds(NewsConstants.FEEDS_NO_PER_CATEGORY, new AsyncCallback>() { @Override public void onSuccess(ArrayList feeds) { if (feeds != null && allUpdates.size() > 0) { Date myLastUpdateTime = allUpdates.get(0).getFeed().getTime(); //this is the last update in the View GWT.log("Last Mine: "+allUpdates.get(0).getFeed().getDescription()); GWT.log("Last Retr.: "+feeds.get(0).getFeed().getDescription()); tempCacheNewUpdates = new ArrayList(); //need to clear it everytime i check (in case someone deleted the updated in the meanwhile) //check if there are new updates (enter the while) and put them in a temporary cache for displaying on user click int i = 0; while (i < feeds.size() && feeds.get(i).getFeed().getTime().after(myLastUpdateTime)) { tempCacheNewUpdates.add(feeds.get(i)); i++; } /* currNewUpdatesNo keeps the number of updates to be added on user clicks, * i keeps the total number as it arrives, * if they differ you got to refresh the updates to show the new number */ if (currNewUpdatesNo < i) { //add the current "show new updates" alert panel if not present if (newsFeedAlert == null) { newsFeedAlert = new NewFeedsAvailable(i, eventBus); newUpdatesPanel.add(newsFeedAlert); } else //update it otherwise newsFeedAlert.updateNewUpdatesNo(i); currNewUpdatesNo = i; } } } @Override public void onFailure(Throwable caught) {} }); } } }); } /** * get the hashtagged feeds * @param hashtag to look for */ private void showFeedsByHashtag(final String hashtag) { showLoader(); newsService.getUserSettings(new AsyncCallback() { @Override public void onFailure(Throwable caught) { doStopFeedsTimer(); } @Override public void onSuccess(UserSettings result) { if (result.getUserInfo().getUsername().equals(NewsConstants.TEST_USER)) { doStopFeedsTimer(); doShowSessionExpired(); } else { /** * this check avoids the 2 tabs open in 2 different scope, if the previous tab was open at VRE Level and then antoher * is open at infra level the first tab stops checking for updates */ if (result.getCurrentScope().compareTo(currentScope) == 0) { newsService.getPostsByHashtag(hashtag, new AsyncCallback>() { @Override public void onSuccess(ArrayList posts) { filterPanelWrapper.setVisible(false); newsPanel.clear(); if (posts != null) { if (posts.size() == 0) { newsPanel.add(new ResultsFor("results for", hashtag)); newsPanel.add(new HTML("
" + "Sorry, looks like we found no updates with topic: " + hashtag +"
")); isFirstTweet = true; } else { newsPanel.setHeight(""); newsPanel.setHorizontalAlignment(HasAlignment.ALIGN_LEFT); newsPanel.setVerticalAlignment(HasVerticalAlignment.ALIGN_TOP); newsPanel.add(new ResultsFor("results for", hashtag)); for (EnhancedFeed feed : posts) { newsPanel.add(new TweetTemplate(false, showFeedTimelineSource, myUserInfo, feed, eventBus)); //in the view } if (posts.size() < 5) { newsPanel.add(new Image(spacer)); } isFirstTweet = false; } } else { showProblems(); } } @Override public void onFailure(Throwable caught) { showProblems(); } }); } } } }); } /** * Called when a user wants to show some feeds related to her statistics */ private void showUserStatisticsRelatedFeeds(final ShowUserStatisticAction relatedFeedsToUserStatisticsToShow, final int from, final int to) { // show loader while waiting showLoader(); // stop asking for feeds doStopFeedsTimer(); final String actionToPrint = relatedFeedsToUserStatisticsToShow.getHumanFriendlyAction(); newsService.getUserSettings(new AsyncCallback() { @Override public void onFailure(Throwable caught) { } @Override public void onSuccess(UserSettings result) { if (result.getUserInfo().getUsername().equals(NewsConstants.TEST_USER)) { doShowSessionExpired(); } else { /** * this check avoids the 2 tabs open in 2 different scope, if the previous tab was open at VRE Level and then antoher * is open at infra level the first tab stops checking for updates */ if (result.getCurrentScope().compareTo(currentScope) == 0) { newsService.getPostsRelatedToUserStatistics(relatedFeedsToUserStatisticsToShow, from, to, new AsyncCallback>() { @Override public void onSuccess(ArrayList posts) { filterPanelWrapper.setVisible(false); newsPanel.clear(); if (posts != null) { GWT.log("Retrieved " + posts.size() + " hits for search."); if (posts.size() == 0) { newsPanel.add(new ResultsFor("", actionToPrint)); newsPanel.add(new HTML("
" + "Sorry, looks like we found no match for action : " + actionToPrint +"
")); } else { newsPanel.setHeight(""); newsPanel.setHorizontalAlignment(HasAlignment.ALIGN_LEFT); newsPanel.setVerticalAlignment(HasVerticalAlignment.ALIGN_TOP); newsPanel.add(new ResultsFor("", actionToPrint)); for (EnhancedFeed feed : posts) { newsPanel.add(new TweetTemplate(false, showFeedTimelineSource, myUserInfo, feed, eventBus)); //in the view // save them (they will be used when asking more feeds) allUpdates.add(feed); } if (posts.size() < 5) { newsPanel.add(new Image(spacer)); } // add widget to lookup more feeds: if the size of the returned data is less // than the required disable this feature. if(posts.size() == FEEDS_RELATED_TO_USER_STATISTICS_TO_SHOW){ showMoreWidget = new ShowMoreFeeds(eventBus); showMoreUpdatesPanel.add(showMoreWidget); newsPanel.add(showMoreUpdatesPanel); } } } else { showProblems(); } } @Override public void onFailure(Throwable caught) { showProblems(); } }); } } } }); } /** * Called when a user search something */ private void showFeedsSearch(final String query, final int from, final int to) { // show loader while waiting showLoader(); // stop asking for feeds doStopFeedsTimer(); newsService.getUserSettings(new AsyncCallback() { @Override public void onFailure(Throwable caught) { } @Override public void onSuccess(UserSettings result) { if (result.getUserInfo().getUsername().equals(NewsConstants.TEST_USER)) { doShowSessionExpired(); } else { /** * this check avoids the 2 tabs open in 2 different scope, if the previous tab was open at VRE Level and then antoher * is open at infra level the first tab stops checking for updates */ if (result.getCurrentScope().compareTo(currentScope) == 0) { newsService.getPostsByQuery(query, from, to, new AsyncCallback>() { @Override public void onSuccess(ArrayList posts) { filterPanelWrapper.setVisible(false); newsPanel.clear(); if (posts != null) { GWT.log("Retrieved " + posts.size() + " hits for search."); if (posts.size() == 0) { newsPanel.add(new ResultsFor("Results for query: ", query)); newsPanel.add(new HTML("
" + "Sorry, looks like we found no match for: " + query +"
")); } else { newsPanel.setHeight(""); newsPanel.setHorizontalAlignment(HasAlignment.ALIGN_LEFT); newsPanel.setVerticalAlignment(HasVerticalAlignment.ALIGN_TOP); newsPanel.add(new ResultsFor("Results for query: ", query)); for (EnhancedFeed feed : posts) { newsPanel.add(new TweetTemplate(false, showFeedTimelineSource, myUserInfo, feed, eventBus)); //in the view // save them (they will be used when asking more feeds) allUpdates.add(feed); } if (posts.size() < 5) { newsPanel.add(new Image(spacer)); } // add widget to lookup more feeds: if the size of the returned data is less // than the required disable this feature. if(posts.size() == SEARCHED_FEEDS_TO_SHOW){ showMoreWidget = new ShowMoreFeeds(eventBus); showMoreUpdatesPanel.add(showMoreWidget); newsPanel.add(showMoreUpdatesPanel); } } } else { showProblems(); } } @Override public void onFailure(Throwable caught) { showProblems(); } }); } } } }); } /** * called when a user click on the are new updates */ protected void doShowCachedNewUpdates() { newUpdatesPanel.clear(); //remove the alert panel newsFeedAlert = null; //reset the alert panel and other needed vars currNewUpdatesNo = 0; //need to put them in reverse order; for (int i = tempCacheNewUpdates.size(); i > 0; i--) { EnhancedFeed feed = tempCacheNewUpdates.get(i-1); final TweetTemplate tt = new TweetTemplate(myUserInfo, feed, eventBus, true); newsPanel.insert(tt, 0); //insert in the view allUpdates.add(0, feed); //insert in the model //timer for the transition Timer t = new Timer() { @Override public void run() { tt.setcontentAreaStyle("visible"); } }; t.schedule(100); } //after that I remove the ($updatesNo) from Window Title String currTitle = Document.get().getTitle(); Document.get().setTitle(currTitle.startsWith("(") ? currTitle.substring(4) : currTitle); } /** * used when adding directly a feed from the UI (IPC) * @param userid * @param fullName * @param thumbURL * @param description */ public void addJustAddedFeed(ClientPost cFeed) { // build up the feed Feed feed = new Feed( cFeed.key, FeedType.SHARE, cFeed.userid, cFeed.time, "", cFeed.uri, cFeed.linkUrlThumbnail, cFeed.description, PrivacyLevel.CONNECTION, cFeed.fullName, cFeed.email, cFeed.thumbnailURL, cFeed.linkTitle, cFeed.linkDescription, cFeed.linkHost); // set multi-attachments property boolean multiAttachments = (cFeed.attachments != null) ? cFeed.attachments.length > 0 : false; feed.setMultiFileUpload(multiAttachments); //false because he could not have liked this yet and true because is the current user's EnhancedFeed toAdd = new EnhancedFeed(feed, false, true); ArrayList attachments = null; if (multiAttachments) { attachments = new ArrayList<>(cFeed.attachments.length); for (int i = 0; i >() { @Override public void onSuccess(ArrayList feeds) { newsPanel.clear(); if (feeds != null) { if (feeds.size() == 0) { if (!isInfrastructure) { newsPanel.add(new HTML("
" + "Looks like nobody posted anything yet. Are you willing to be the first?
" + "You may begin by posting a message!
")); } else { newsPanel.add(new HTML("
" + "Looks like we've got nothing for you at the moment.
" + "You may begin by joining some of the available " + "
"+vreLabel+"s.
")); } isFirstTweet = true; } else { newsPanel.setHeight(""); newsPanel.setHorizontalAlignment(HasAlignment.ALIGN_LEFT); newsPanel.setVerticalAlignment(HasVerticalAlignment.ALIGN_TOP); for (EnhancedFeed feed : feeds) { newsPanel.add(new TweetTemplate(false, showFeedTimelineSource, myUserInfo, feed, eventBus)); //in the view allUpdates.add(feed); //in the model } if (feeds.size() < 5) { newsPanel.add(new Image(spacer)); } //if you are showing more than feedsMaxPerCategory-1 feeds there is probably more if (feeds.size() >= NewsConstants.FEEDS_MAX_PER_CATEGORY-1 && (!isInfrastructure)) { GWT.log("Show MORE " + NewsConstants.FEEDS_NO_PER_CATEGORY); showMoreWidget = new ShowMoreFeeds(eventBus); showMoreUpdatesPanel.add(showMoreWidget); newsPanel.add(showMoreUpdatesPanel); } isFirstTweet = false; } } else { showProblems(); } } @Override public void onFailure(Throwable caught) { newsPanel.clear(); newsPanel.add(new HTML("
" + "Ops! There were problems while retrieving your feeds!.
" + "Please try again in a short while.
")); } }); } /** * called when a user scroll down the page to the bottom */ protected void doShowMoreUpdates() { showMoreUpdatesPanel.remove(0); loadingImage.getElement().getStyle().setMargin(10, Unit.PX); showMoreUpdatesPanel.add(loadingIcon); if(isSearch){ GWT.log("Going to request more feeds for this search"); // start position int start = allUpdates.size(); GWT.log("StartingPoint = " + start); newsService.getPostsByQuery(currentQuery, start, SEARCHED_FEEDS_TO_SHOW , new AsyncCallback>() { @Override public void onSuccess(ArrayList posts){ newsPanel.remove(showMoreUpdatesPanel); if (posts != null) { GWT.log("There are " + posts.size() + " more feeds"); for (EnhancedFeed feed : posts) { // avoid to insert same data if(!isFeedPresent(feed)){ newsPanel.add(new TweetTemplate(false, showFeedTimelineSource, myUserInfo, feed, eventBus)); //in the view allUpdates.add(feed); } } // clear panel showMoreUpdatesPanel.clear(); // check if we can ask for other data if(posts.size() == SEARCHED_FEEDS_TO_SHOW){ GWT.log("It seems there are no more feeds for this query. Stop asking further"); showMoreWidget = new ShowMoreFeeds(eventBus); showMoreUpdatesPanel.add(showMoreWidget); newsPanel.add(showMoreUpdatesPanel); }else{ showMoreWidget = null; } } } @Override public void onFailure(Throwable caught) { showMoreUpdatesPanel.clear(); newsPanel.add(new HTML("
" + "Ops! There were problems while retrieving your feeds!.
" + "Please try again in a short while.
")); } }); }else if(isFeedsRelatedToStatistics){ GWT.log("Going to request more feeds for this user's statisics"); // start position int start = allUpdates.size(); GWT.log("StartingPoint = " + start); newsService.getPostsRelatedToUserStatistics(relatedFeedsToUserStatisticsToShow, start, FEEDS_RELATED_TO_USER_STATISTICS_TO_SHOW , new AsyncCallback>() { @Override public void onSuccess(ArrayList posts){ newsPanel.remove(showMoreUpdatesPanel); if (posts != null) { GWT.log("There are " + posts.size() + " more feeds"); for (EnhancedFeed feed : posts) { // avoid to insert same data if(!isFeedPresent(feed)){ newsPanel.add(new TweetTemplate(false, showFeedTimelineSource, myUserInfo, feed, eventBus)); //in the view allUpdates.add(feed); } } // clear panel showMoreUpdatesPanel.clear(); // check if we can ask for other data if(posts.size() == FEEDS_RELATED_TO_USER_STATISTICS_TO_SHOW){ GWT.log("It seems there are no more feeds for this query. Stop asking further"); showMoreWidget = new ShowMoreFeeds(eventBus); showMoreUpdatesPanel.add(showMoreWidget); newsPanel.add(showMoreUpdatesPanel); }else{ showMoreWidget = null; } } } @Override public void onFailure(Throwable caught) { showMoreUpdatesPanel.clear(); newsPanel.add(new HTML("
" + "Ops! There were problems while retrieving your feeds!.
" + "Please try again in a short while.
")); } }); } else{ final int quantity = 10; loadMorePosts(quantity, false); } } /** * * @param quantity */ public void loadMorePosts(final int quantity, boolean isSortingByLastCommentedposts) { int from = (fromStartingPoint == null) ? allUpdates.size()+1 : fromStartingPoint; if (isSortingByLastCommentedposts) { showLoader(); if ((from == 0)) from = 1; } GWT.log("StartingPoint = " + from); newsService.getMorePosts(from, quantity, new AsyncCallback() { @Override public void onSuccess(MorePostsBean rangeFeeds) { newsPanel.remove(showMoreUpdatesPanel); if (rangeFeeds.getPosts() != null) { fromStartingPoint = rangeFeeds.getLastReturnedFeedTimelineIndex(); int c = 1; for (EnhancedFeed post : rangeFeeds.getPosts()) { if (!isFeedPresent(post)) { //avoid possible duplicates newsPanel.add(new TweetTemplate(false, showFeedTimelineSource, myUserInfo, post, eventBus)); //in the view allUpdates.add(post); //in the model } c++; } if (c >= quantity) { //there could be more feeds GWT.log("there could be more feeds"); showMoreUpdatesPanel.clear(); showMoreWidget = new ShowMoreFeeds(eventBus); showMoreUpdatesPanel.add(showMoreWidget); newsPanel.add(showMoreUpdatesPanel); } if (isSortingByLastCommentedposts) { sortCurrentPostsByLatestComment(); showMoreUpdatesPanel.clear(); showMoreWidget = null; if (getAllUpdatesSize() > 99) newsPanel.add(new HTML(LIMIT_REACHED_FOR_SORTING_COMMENTS_TEXT)); } } } @Override public void onFailure(Throwable caught) { showMoreUpdatesPanel.clear(); newsPanel.add(new HTML("
" + "Ops! There were problems while retrieving your feeds!.
" + "Please try again in a short while.
")); } }); } /** * @param widget the widget to check * @returnn true if the widget is in the visible part of the page */ private boolean isScrolledIntoView(Widget widget) { if (widget != null) { int docViewTop = Window.getScrollTop(); int docViewBottom = docViewTop + Window.getClientHeight(); int elemTop = widget.getAbsoluteTop(); int elemBottom = elemTop + widget.getOffsetHeight(); return ((elemBottom <= docViewBottom) && (elemTop >= docViewTop)); } return false; } private boolean isFeedPresent(EnhancedFeed toCheck) { for (EnhancedFeed feed : allUpdates) { if (feed.getFeed().getKey().compareTo(toCheck.getFeed().getKey()) == 0) return true; } return false; } /** * Only User Connections */ public void showOnlyConnectionsFeeds() { showLoader(); newsService.getOnlyConnectionsUserPosts(new AsyncCallback>() { @Override public void onSuccess(ArrayList feeds) { if (feeds != null) { newsPanel.clear(); if (feeds.size() == 0) { //TODO: provide actual link for Making some friends newsPanel.add(new HTML("
" + "Looks like we've got nothing for you at the moment.
" + "You may begin by adding some friend!
")); isFirstTweet = true; } else { newsPanel.setHeight(""); newsPanel.setHorizontalAlignment(HasAlignment.ALIGN_LEFT); newsPanel.setVerticalAlignment(HasVerticalAlignment.ALIGN_TOP); for (EnhancedFeed feed : feeds) newsPanel.add(new TweetTemplate(false, false, myUserInfo, feed, eventBus)); if (feeds.size() < 5) { newsPanel.add(new Image(spacer)); } isFirstTweet = false; } } else showProblems(); } @Override public void onFailure(Throwable caught) { loadingImage.setUrl(warning); newsPanel.add(new HTML("Ops! There were problems while retrieving your feeds! Please try again in a short while")); } }); } /** * Only User Feeds */ public void showOnlyMyPosts() { showLoader(); newsService.getOnlyMyUserPosts(new AsyncCallback>() { @Override public void onSuccess(ArrayList posts) { if (posts != null) { newsPanel.clear(); if (posts.size() == 0) { newsPanel.add(new HTML("
" + "Looks like we've got nothing for you at the moment.
" + "You may begin by sharing an update!
")); isFirstTweet = true; } else { newsPanel.setHeight(""); newsPanel.setHorizontalAlignment(HasAlignment.ALIGN_LEFT); newsPanel.setVerticalAlignment(HasVerticalAlignment.ALIGN_TOP); for (EnhancedFeed feed : posts) newsPanel.add(new TweetTemplate(false, showFeedTimelineSource, myUserInfo, feed, eventBus)); if (posts.size() < 5) { newsPanel.add(new Image(spacer)); } isFirstTweet = false; } } else showProblems(); } @Override public void onFailure(Throwable caught) { loadingImage.setUrl(warning); newsPanel.add(new HTML("Ops! There were problems while retrieving your feeds! Please try again in a short while")); } }); } /** * Only User Liked Posts */ public void showOnlyLikedPosts() { showLoader(); newsService.getOnlyLikedPosts(new AsyncCallback>() { @Override public void onSuccess(ArrayList posts) { if (posts != null) { newsPanel.clear(); if (posts.size() == 0) { newsPanel.add(new HTML("
" + "Looks like we've got nothing for you at the moment.
" + "Set an update as your favorite to see it here
")); isFirstTweet = true; } else { newsPanel.setHeight(""); newsPanel.setHorizontalAlignment(HasAlignment.ALIGN_LEFT); newsPanel.setVerticalAlignment(HasVerticalAlignment.ALIGN_TOP); for (EnhancedFeed feed : posts) newsPanel.add(new TweetTemplate(false, showFeedTimelineSource, myUserInfo, feed, eventBus)); if (posts.size() < 5) { newsPanel.add(new Image(spacer)); } isFirstTweet = false; } } else showProblems(); } @Override public void onFailure(Throwable caught) { loadingImage.setUrl(warning); newsPanel.add(new HTML("Ops! There were problems while retrieving your feeds! Please try again in a short while")); } }); } /**: * Sort the posts per recent comment */ private void sortCurrentPostsByLatestComment() { showLoader(); GWT.log(" number of posts " + allUpdates.size()); ArrayList latestComments = new ArrayList<>(); //thePostsMap is need so that later it is faster to get the post by id HashMap theCommentedPostsMap = new HashMap<>(); for (EnhancedFeed post : allUpdates) { ArrayList postComments = post.getComments(); if (postComments != null && !postComments.isEmpty()) { theCommentedPostsMap.put(post.getFeed().getKey(), post); Comment latest = postComments.get(postComments.size()-1); latestComments.add(latest); //get the latest } } //sort the comments Collections.sort(latestComments, Collections.reverseOrder()); ArrayList sortedPostsByLatestComment = new ArrayList<>(); // evaluate unique posts' ids HashSet postIds = new HashSet(); for (Comment comment : latestComments) { String postId = comment.getFeedid(); if(!postIds.contains(postId)){ postIds.add(postId); sortedPostsByLatestComment.add(theCommentedPostsMap.get(postId)); } } //at this point the sortedPostsByLatestComment list contains only the commented posts in the right order //we need to add the remaining posts of the page in the latest post order for (EnhancedFeed post : allUpdates) { if (!theCommentedPostsMap.containsKey(post.getFeed().getKey())) sortedPostsByLatestComment.add(post); } newsPanel.clear(); if (sortedPostsByLatestComment.size() == 0) { newsPanel.add(new HTML("
" + "Looks like we've got nothing for you at the moment.
" + "Set an update as your favorite to see it here
")); isFirstTweet = true; } else { newsPanel.setHeight(""); newsPanel.setHorizontalAlignment(HasAlignment.ALIGN_LEFT); newsPanel.setVerticalAlignment(HasVerticalAlignment.ALIGN_TOP); for (EnhancedFeed feed : sortedPostsByLatestComment) newsPanel.add(new TweetTemplate(false, showFeedTimelineSource, myUserInfo, feed, eventBus)); if (sortedPostsByLatestComment.size() < 5) { newsPanel.add(new Image(spacer)); } isFirstTweet = false; } } private void showLoader() { newsPanel.clear(); newsPanel.setWidth("100%"); newsPanel.setHeight("300px"); newsPanel.setHorizontalAlignment(HasAlignment.ALIGN_CENTER); newsPanel.setVerticalAlignment(HasVerticalAlignment.ALIGN_MIDDLE); newsPanel.add(new Image(loading)); } private void showProblems() { newsPanel.clear(); newsPanel.add(new HTML("
" + "Ops! There were problems while retrieving your feeds!.
" + "Looks like we are not able to communicate with the infrastructure,
(or your session expired)
please try again in a short while or refresh the page.
")); } private void doAddLike(final TweetTemplate owner, final String feedId) { newsService.like(feedId, owner.getMyFeedText(), owner.getMyFeedUserId(), new AsyncCallback() { @Override public void onFailure(Throwable caught) {} @Override public void onSuccess(Boolean result) { if (!result) { doShowSessionExpired(); } } }); } protected void doUnLike(final TweetTemplate owner, String feedId) { newsService.unlike(feedId, owner.getMyFeedText(), owner.getMyFeedUserId(), new AsyncCallback() { @Override public void onFailure(Throwable caught) {} @Override public void onSuccess(Boolean result) { if (!result) { doShowSessionExpired(); } } }); } private void doShowSessionExpired() { } private void doShowLikes(final String postId) { final UserSelectionDialog dlg = new UserSelectionDialog("People who set this as Favorite", eventBus); dlg.center(); dlg.show(); newsService.getAllLikesByPost(postId, new AsyncCallback>() { @Override public void onFailure(Throwable caught) { Window.alert("People who liked this could not be retrieved: " + caught.getMessage()); } @Override public void onSuccess(ArrayList result) { ArrayList toShow = new ArrayList(); for (Like like : result) { toShow.add(new ItemSelectableBean(like.getUserid(), like.getFullName(), like.getThumbnailURL())); } eventBus.fireEvent(new UsersFetchedEvent(toShow)); } }); } private void doAddComment(final TweetTemplate owner, String text, HashSet mentionedUsers) { owner.showCommentingPreloader(true); newsService.comment(owner.getFeedKey(), text, mentionedUsers, owner.getMyFeedUserId(), owner.isAppFeed(), new AsyncCallback() { @Override public void onFailure(Throwable caught) { owner.showCommentingPreloader(true); Window.alert("Could not deliver this comment: " + caught.getMessage()); } @Override public void onSuccess(OperationResult result) { owner.showCommentingPreloader(false); if (result != null) { if (!result.isSuccess()) { Window.alert("Could not deliver this comment. Please try again in a short while."); } else { Comment comment = (Comment) result.getComment(); owner.addComment(new SingleComment(comment, owner, (comment.getUserid().equals(myUserInfo.getUsername())))); owner.setCommentingDisabled(false); owner.updateCommentsNumberCount(); owner.showAddCommentForm(false); // if(owner.isUser()){ // // alert the User statistics portlet to increment the number of comments got // try { // NewsFeed.pageBusAdapter.PageBusPublish(PageBusEvents.commentsIncrement, "", Defaults.STRING_JSONIZER); // } catch (PageBusAdapterException e) { // GWT.log(e.toString()); // } // } } } else { Window.alert("Could not deliver this comment. Please try again in a short while."); } } }); } private void doEditComment(final TweetTemplate owner, Comment edited, final HTMLPanel commentPanel) { newsService.editComment(edited, new AsyncCallback() { @Override public void onFailure(Throwable caught) { Window.alert("Could not edit this comment: " + caught.getMessage()); } @Override public void onSuccess(OperationResult result) { if (result != null) { if (!result.isSuccess()) { //CheckSession.showLogoutDialog(); } else { Comment comment = (Comment) result.getComment(); owner.updateSingleComment(comment, commentPanel); owner.setCommentingDisabled(false); } } else { Window.alert("Could not deliver this comment. Please try again in a short while."); } } }); } private void doShowComments(final TweetTemplate owner, final boolean commentForm2Add) { owner.showLoadingComments(); newsService.getAllCommentsByPost(owner.getFeedKey(), new AsyncCallback>() { @Override public void onFailure(Throwable caught) { Window.alert("Comments could not be retrieved: " + caught.getMessage()); } @Override public void onSuccess(ArrayList comments) { owner.clearComments(); for (Comment comment :comments) owner.addComment(new SingleComment(comment, owner,(comment.getUserid().equals(myUserInfo.getUsername())))); owner.setCommentsFetched(true); if (commentForm2Add) owner.showAddCommentForm(false); owner.updateCommentsNumberCount(); owner.showAddCommentForm(false); } }); } protected void doDeleteComment(final TweetTemplate owner, String commentId) { newsService.deleteComment(commentId, owner.getFeedKey(), new AsyncCallback() { @Override public void onFailure(Throwable caught) { Window.alert("Comment could not be deleted: " + caught.getMessage()); } @Override public void onSuccess(Boolean result) { if (result) { doShowComments(owner, false); owner.updateCommentsNumberCount(); } else Window.alert("Comment could not be deleted, please try again in a short while."); } }); } private void doDeletePost(final TweetTemplate toDelete) { if (Window.confirm("Are you sure you want to delete this post?")) { newsService.deletePost(toDelete.getFeedKey(), new AsyncCallback() { @Override public void onFailure(Throwable caught) { Window.alert("Feed could not be deleted: " + caught.getMessage()); } @Override public void onSuccess(Boolean result) { if (result) { toDelete.removeFromParent(); } else Window.alert("Feed could not be deleted, please try again in a short while."); } }); } } private void doShowFeed(final TweetTemplate toShow) { String feedKey = toShow.getFeedKey(); Window.Location.assign(Window.Location.getHref() + ((Window.Location.getHref().contains("?")) ? "&oid="+feedKey : "?oid="+feedKey)); } public int getAllUpdatesSize() { return allUpdates.size(); } /** * set the filter type status for automatic reloading of tweets * @param currentFilter */ public void setCurrentFilter(FilterType currentFilter) { this.currentFilter = currentFilter; } /** * * @return the current scope on the client */ public static String getCurrentScope() { return currentScope; } public static String extractOrgFriendlyURL(String portalURL) { String groupRegEx = "/group/"; if (portalURL.contains(groupRegEx)) { String[] splits = portalURL.split(groupRegEx); String friendlyURL = splits[1]; if (friendlyURL.contains("/")) { friendlyURL = friendlyURL.split("/")[0]; } else { friendlyURL = friendlyURL.split("\\?")[0].split("\\#")[0]; } return "/"+friendlyURL; } return null; } }