Java tutorial
/* * Copyright 2014 Vaadin Ltd. * * 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.vaadin.tori.component; import org.ocpsoft.prettytime.PrettyTime; import org.vaadin.tori.ToriApiLoader; import org.vaadin.tori.ToriNavigator; import org.vaadin.tori.ToriUI; import org.vaadin.tori.data.DataSource; import org.vaadin.tori.data.entity.DiscussionThread; import org.vaadin.tori.data.entity.Post; import org.vaadin.tori.exception.DataSourceException; import org.vaadin.tori.util.ComponentUtil; import org.vaadin.tori.util.ComponentUtil.HeadingLevel; import org.vaadin.tori.util.ToriActivityMessaging; import org.vaadin.tori.util.ToriActivityMessaging.UserAuthoredListener; import org.vaadin.tori.util.ToriScheduler; import org.vaadin.tori.util.ToriScheduler.ScheduledCommand; import org.vaadin.tori.view.listing.SpecialCategory; import com.vaadin.server.ExternalResource; import com.vaadin.shared.ui.MarginInfo; import com.vaadin.ui.Alignment; import com.vaadin.ui.CssLayout; import com.vaadin.ui.CustomComponent; import com.vaadin.ui.HorizontalLayout; import com.vaadin.ui.Label; import com.vaadin.ui.Link; @SuppressWarnings("serial") public class RecentBar extends CustomComponent implements UserAuthoredListener { private PostNotification previous; private PostNotification current; private final FloatingNotification floatingNotification = new FloatingNotification(); private final FloatingComponent floatingComponent = new FloatingComponent(); private final DataSource dataSource = ToriApiLoader.getCurrent().getDataSource(); private CssLayout notificationsLayout; private final ToriActivityMessaging messaging = ToriApiLoader.getCurrent().getToriActivityMessaging(); public RecentBar() { if (messaging != null) { messaging.addUserAuthoredListener(this); ToriUI.getCurrent().addDetachListener(new DetachListener() { @Override public void detach(final DetachEvent event) { messaging.removeUserAuthoredListener(RecentBar.this); } }); } addStyleName("recentbar"); setWidth(100.0f, Unit.PERCENTAGE); setHeight(35.0f, Unit.PIXELS); CssLayout layout = new CssLayout(); layout.setSizeFull(); setCompositionRoot(layout); HorizontalLayout barLayout = new HorizontalLayout(); barLayout.setMargin(new MarginInfo(false, true, false, true)); barLayout.setSizeFull(); addTitleLabel(barLayout); addNotificationsLayout(barLayout); addRecentLink(barLayout); if (dataSource.isLoggedInUser()) { addMyPostsLink(barLayout); } layout.addComponent(barLayout); floatingComponent.extend(floatingNotification); layout.addComponent(floatingNotification); floatingNotification.setId("floatingnotification"); ToriScheduler.get().scheduleDeferred(new ScheduledCommand() { @Override public void execute() { refresh(); } }); } private void addMyPostsLink(final HorizontalLayout barLayout) { Link link = new Link("My Posts", new ExternalResource("#" + ToriNavigator.ApplicationView.CATEGORIES.getUrl() + "/" + SpecialCategory.MY_POSTS.getId().toLowerCase())); link.addStyleName("mypostslink"); barLayout.addComponent(link); barLayout.setComponentAlignment(link, Alignment.MIDDLE_RIGHT); } private void addRecentLink(final HorizontalLayout barLayout) { Link link = new Link("Recent Posts", new ExternalResource("#" + ToriNavigator.ApplicationView.CATEGORIES.getUrl() + "/" + SpecialCategory.RECENT_POSTS.getId().toLowerCase())); link.addStyleName("recentlink"); barLayout.addComponent(link); barLayout.setComponentAlignment(link, Alignment.MIDDLE_RIGHT); } private void addNotificationsLayout(final HorizontalLayout barLayout) { notificationsLayout = new CssLayout(); notificationsLayout.setSizeFull(); notificationsLayout.addStyleName("notificationslayout"); barLayout.addComponent(notificationsLayout); barLayout.setExpandRatio(notificationsLayout, 1.0f); barLayout.setComponentAlignment(notificationsLayout, Alignment.MIDDLE_LEFT); } private void addTitleLabel(final HorizontalLayout barLayout) { Label headingLabel = ComponentUtil.getHeadingLabel("MOST RECENT", HeadingLevel.H4); headingLabel.setWidth(110.0f, Unit.PIXELS); barLayout.addComponent(headingLabel); barLayout.setComponentAlignment(headingLabel, Alignment.MIDDLE_LEFT); } @Override public void userAuthored(final long postId, final long threadId) { try { getUI().access(new Runnable() { @Override public void run() { try { refresh(); } catch (RuntimeException e) { // Ignore } } }); } catch (RuntimeException e) { // Ignore } } private void newPostAdded(final Post post) { if (previous != null) { notificationsLayout.removeComponent(previous); } previous = current; current = new PostNotification(post); notificationsLayout.addComponent(current); floatingNotification.setLink(post.getThread().getTopic(), PostNotification.getPermaLinkUrl(post)); floatingComponent.flash(); ToriScheduler.get().scheduleDeferred(new ScheduledCommand() { @Override public void execute() { previous.setState(PostNotificationState.PREVIOUS); current.setState(PostNotificationState.CURRENT); } }); } private enum PostNotificationState { PREVIOUS, CURRENT, NEXT } public static class PostNotification extends HorizontalLayout { private final PrettyTime prettyTime = new PrettyTime(); private final long postId; public PostNotification(final Post post) { this.postId = post.getId(); setSpacing(true); Link link = new Link(post.getThread().getTopic(), new ExternalResource(getPermaLinkUrl(post))); StringBuilder infoText = new StringBuilder("by "); infoText.append(post.getAuthor().getDisplayedName()).append(", ") .append(prettyTime.format(post.getTime())); Label info = new Label(infoText.toString()); addComponents(link, info); setState(PostNotificationState.NEXT); } public void setState(final PostNotificationState state) { setStyleName("postnotification " + state.name().toLowerCase()); } private static String getPermaLinkUrl(final Post post) { // @formatter:off final String linkUrl = String.format("#%s/%s/%s", ToriNavigator.ApplicationView.THREADS.getUrl(), post.getThread().getId(), post.getId()); // @formatter:on return linkUrl; } public long getPostId() { return postId; } } public static class FloatingNotification extends CssLayout { private final Link link; public FloatingNotification() { addStyleName("floatingnotification"); Label label = new Label("New post in"); label.setSizeUndefined(); addComponent(label); link = new Link(); addComponent(link); } public void setLink(final String caption, final String url) { link.setCaption(caption); link.setResource(new ExternalResource(url)); } } public static RecentBar getCurrent() { return ToriUI.getCurrent().getRecentBar(); } public void refresh() { try { DiscussionThread recentThread = null; try { int count = dataSource.getThreadCountRecursively(null); for (int i = 0; i < count; i++) { DiscussionThread thread = dataSource.getRecentPosts(i, Math.min(i + 1, count - 1)).get(0); boolean newerThread = recentThread == null || thread.getLatestPost().getTime().after(recentThread.getLatestPost().getTime()); recentThread = newerThread ? thread : recentThread; if (!thread.isSticky()) { break; } } } catch (IndexOutOfBoundsException e) { // No recent threads } if (recentThread != null) { Post latestPost = recentThread.getLatestPost(); if (latestPost != null) { latestPost.setThread(recentThread); if (current == null) { current = new PostNotification(latestPost); current.setState(PostNotificationState.CURRENT); notificationsLayout.addComponent(current); } else { long latestId = latestPost.getId(); long currentId = current.getPostId(); if (latestId != currentId) { newPostAdded(latestPost); } } } } } catch (DataSourceException e) { e.printStackTrace(); } } }