diff --git a/src/main/java/com/commafeed/backend/cache/CacheService.java b/src/main/java/com/commafeed/backend/cache/CacheService.java index 13c19c00..a470d3c4 100644 --- a/src/main/java/com/commafeed/backend/cache/CacheService.java +++ b/src/main/java/com/commafeed/backend/cache/CacheService.java @@ -9,6 +9,7 @@ import com.commafeed.backend.model.FeedEntry; import com.commafeed.backend.model.FeedSubscription; import com.commafeed.backend.model.User; import com.commafeed.frontend.model.Category; +import com.commafeed.frontend.model.UnreadCount; public abstract class CacheService { @@ -29,9 +30,9 @@ public abstract class CacheService { public abstract void invalidateUserRootCategory(User... users); // unread count - public abstract Long getUnreadCount(FeedSubscription sub); + public abstract UnreadCount getUnreadCount(FeedSubscription sub); - public abstract void setUnreadCount(FeedSubscription sub, Long count); + public abstract void setUnreadCount(FeedSubscription sub, UnreadCount count); public abstract void invalidateUnreadCount(FeedSubscription... subs); diff --git a/src/main/java/com/commafeed/backend/cache/NoopCacheService.java b/src/main/java/com/commafeed/backend/cache/NoopCacheService.java index 39f7c0de..e8755090 100644 --- a/src/main/java/com/commafeed/backend/cache/NoopCacheService.java +++ b/src/main/java/com/commafeed/backend/cache/NoopCacheService.java @@ -10,6 +10,7 @@ import com.commafeed.backend.model.Feed; import com.commafeed.backend.model.FeedSubscription; import com.commafeed.backend.model.User; import com.commafeed.frontend.model.Category; +import com.commafeed.frontend.model.UnreadCount; @Alternative @ApplicationScoped @@ -25,12 +26,12 @@ public class NoopCacheService extends CacheService { } @Override - public Long getUnreadCount(FeedSubscription sub) { + public UnreadCount getUnreadCount(FeedSubscription sub) { return null; } @Override - public void setUnreadCount(FeedSubscription sub, Long count) { + public void setUnreadCount(FeedSubscription sub, UnreadCount count) { } diff --git a/src/main/java/com/commafeed/backend/cache/RedisCacheService.java b/src/main/java/com/commafeed/backend/cache/RedisCacheService.java index e8d76aa5..51287db2 100644 --- a/src/main/java/com/commafeed/backend/cache/RedisCacheService.java +++ b/src/main/java/com/commafeed/backend/cache/RedisCacheService.java @@ -20,6 +20,7 @@ import com.commafeed.backend.model.FeedSubscription; import com.commafeed.backend.model.Models; import com.commafeed.backend.model.User; import com.commafeed.frontend.model.Category; +import com.commafeed.frontend.model.UnreadCount; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.google.common.collect.Lists; @@ -104,14 +105,14 @@ public class RedisCacheService extends CacheService { } @Override - public Long getUnreadCount(FeedSubscription sub) { - Long count = null; + public UnreadCount getUnreadCount(FeedSubscription sub) { + UnreadCount count = null; Jedis jedis = pool.getResource(); try { String key = buildRedisUnreadCountKey(sub); - String countString = jedis.get(key); - if (countString != null) { - count = Long.valueOf(countString); + String json = jedis.get(key); + if (json != null) { + count = mapper.readValue(json, UnreadCount.class); } } catch (Exception e) { log.error(e.getMessage(), e); @@ -122,14 +123,14 @@ public class RedisCacheService extends CacheService { } @Override - public void setUnreadCount(FeedSubscription sub, Long count) { + public void setUnreadCount(FeedSubscription sub, UnreadCount count) { Jedis jedis = pool.getResource(); try { String key = buildRedisUnreadCountKey(sub); Pipeline pipe = jedis.pipelined(); pipe.del(key); - pipe.set(key, String.valueOf(count)); + pipe.set(key, mapper.writeValueAsString(count)); pipe.expire(key, (int) TimeUnit.MINUTES.toSeconds(30)); pipe.sync(); } catch (Exception e) { diff --git a/src/main/java/com/commafeed/backend/dao/FeedEntryStatusDAO.java b/src/main/java/com/commafeed/backend/dao/FeedEntryStatusDAO.java index cc3d50b5..bf8a4f7e 100644 --- a/src/main/java/com/commafeed/backend/dao/FeedEntryStatusDAO.java +++ b/src/main/java/com/commafeed/backend/dao/FeedEntryStatusDAO.java @@ -39,6 +39,7 @@ import com.commafeed.backend.model.Models; import com.commafeed.backend.model.User; import com.commafeed.backend.model.UserSettings.ReadingOrder; import com.commafeed.backend.services.ApplicationSettingsService; +import com.commafeed.frontend.model.UnreadCount; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; @@ -240,18 +241,21 @@ public class FeedEntryStatusDAO extends GenericDAO { } @SuppressWarnings("unchecked") - public Long getUnreadCount(FeedSubscription subscription) { - Long count = null; + public UnreadCount getUnreadCount(FeedSubscription subscription) { + UnreadCount uc = null; Criteria criteria = buildSearchCriteria(subscription, true, null, null, -1, -1, null, false, null); ProjectionList projection = Projections.projectionList(); projection.add(Projections.rowCount(), "count"); + projection.add(Projections.max(FeedEntry_.updated.getName()), "updated"); criteria.setProjection(projection); criteria.setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP); - List> list = criteria.list(); - for (Map row : list) { - count = row.get("count"); + List> list = criteria.list(); + for (Map row : list) { + Long count = (Long) row.get("count"); + Date updated = (Date) row.get("updated"); + uc = new UnreadCount(subscription.getId(), count, updated); } - return count; + return uc; } private List lazyLoadContent(boolean includeContent, List results) { diff --git a/src/main/java/com/commafeed/backend/services/FeedSubscriptionService.java b/src/main/java/com/commafeed/backend/services/FeedSubscriptionService.java index 801b2757..aa8ffbf0 100644 --- a/src/main/java/com/commafeed/backend/services/FeedSubscriptionService.java +++ b/src/main/java/com/commafeed/backend/services/FeedSubscriptionService.java @@ -21,6 +21,7 @@ import com.commafeed.backend.model.FeedCategory; import com.commafeed.backend.model.FeedSubscription; import com.commafeed.backend.model.Models; import com.commafeed.backend.model.User; +import com.commafeed.frontend.model.UnreadCount; import com.google.common.collect.Maps; public class FeedSubscriptionService { @@ -95,8 +96,8 @@ public class FeedSubscriptionService { } } - public Long getUnreadCount(FeedSubscription sub) { - Long count = cache.getUnreadCount(sub); + public UnreadCount getUnreadCount(FeedSubscription sub) { + UnreadCount count = cache.getUnreadCount(sub); if (count == null) { log.debug("unread count cache miss for {}", Models.getId(sub)); count = feedEntryStatusDAO.getUnreadCount(sub); @@ -105,8 +106,8 @@ public class FeedSubscriptionService { return count; } - public Map getUnreadCount(User user) { - Map map = Maps.newHashMap(); + public Map getUnreadCount(User user) { + Map map = Maps.newHashMap(); List subs = feedSubscriptionDAO.findAll(user); for (FeedSubscription sub : subs) { map.put(sub.getId(), getUnreadCount(sub)); diff --git a/src/main/java/com/commafeed/frontend/model/Subscription.java b/src/main/java/com/commafeed/frontend/model/Subscription.java index 0c30766e..e9aeb940 100644 --- a/src/main/java/com/commafeed/frontend/model/Subscription.java +++ b/src/main/java/com/commafeed/frontend/model/Subscription.java @@ -20,7 +20,7 @@ import com.wordnik.swagger.annotations.ApiProperty; @ApiClass("User information") public class Subscription implements Serializable { - public static Subscription build(FeedSubscription subscription, String publicUrl, long unreadCount) { + public static Subscription build(FeedSubscription subscription, String publicUrl, UnreadCount unreadCount) { Date now = new Date(); FeedCategory category = subscription.getCategory(); Feed feed = subscription.getFeed(); @@ -35,7 +35,8 @@ public class Subscription implements Serializable { sub.setIconUrl(FeedUtils.getFaviconUrl(subscription, publicUrl)); sub.setLastRefresh(feed.getLastUpdated()); sub.setNextRefresh((feed.getDisabledUntil() != null && feed.getDisabledUntil().before(now)) ? null : feed.getDisabledUntil()); - sub.setUnread(unreadCount); + sub.setUnread(unreadCount.getUnreadCount()); + sub.setNewestItemTime(unreadCount.getNewestItemTime()); sub.setCategoryId(category == null ? null : String.valueOf(category.getId())); return sub; } @@ -76,6 +77,9 @@ public class Subscription implements Serializable { @ApiProperty("position of the subscription's in the list") private Integer position; + @ApiProperty("date of the newest item") + private Date newestItemTime; + public Long getId() { return id; } @@ -172,4 +176,12 @@ public class Subscription implements Serializable { this.position = position; } + public Date getNewestItemTime() { + return newestItemTime; + } + + public void setNewestItemTime(Date newestItemTime) { + this.newestItemTime = newestItemTime; + } + } \ No newline at end of file diff --git a/src/main/java/com/commafeed/frontend/model/UnreadCount.java b/src/main/java/com/commafeed/frontend/model/UnreadCount.java index 094a092f..a0d1721a 100644 --- a/src/main/java/com/commafeed/frontend/model/UnreadCount.java +++ b/src/main/java/com/commafeed/frontend/model/UnreadCount.java @@ -1,6 +1,7 @@ package com.commafeed.frontend.model; import java.io.Serializable; +import java.util.Date; import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; @@ -16,14 +17,16 @@ public class UnreadCount implements Serializable { private long feedId; private long unreadCount; + private Date newestItemTime; public UnreadCount() { } - public UnreadCount(long feedId, long unreadCount) { + public UnreadCount(long feedId, long unreadCount, Date newestItemTime) { this.feedId = feedId; this.unreadCount = unreadCount; + this.newestItemTime = newestItemTime; } public long getFeedId() { @@ -42,4 +45,12 @@ public class UnreadCount implements Serializable { this.unreadCount = unreadCount; } + public Date getNewestItemTime() { + return newestItemTime; + } + + public void setNewestItemTime(Date newestItemTime) { + this.newestItemTime = newestItemTime; + } + } diff --git a/src/main/java/com/commafeed/frontend/rest/resources/CategoryREST.java b/src/main/java/com/commafeed/frontend/rest/resources/CategoryREST.java index d4b0d045..fbcfcd02 100644 --- a/src/main/java/com/commafeed/frontend/rest/resources/CategoryREST.java +++ b/src/main/java/com/commafeed/frontend/rest/resources/CategoryREST.java @@ -364,12 +364,8 @@ public class CategoryREST extends AbstractREST { @Path("/unreadCount") @ApiOperation(value = "Get unread count for feed subscriptions", responseClass = "List[com.commafeed.frontend.model.UnreadCount]") public Response getUnreadCount() { - List list = Lists.newArrayList(); - Map unreadCount = feedSubscriptionService.getUnreadCount(getUser()); - for (Map.Entry e : unreadCount.entrySet()) { - list.add(new UnreadCount(e.getKey(), e.getValue())); - } - return Response.ok(list).build(); + Map unreadCount = feedSubscriptionService.getUnreadCount(getUser()); + return Response.ok(Lists.newArrayList(unreadCount.values())).build(); } @GET @@ -386,7 +382,7 @@ public class CategoryREST extends AbstractREST { log.debug("tree cache miss for {}", user.getId()); List categories = feedCategoryDAO.findAll(user); List subscriptions = feedSubscriptionDAO.findAll(user); - Map unreadCount = feedSubscriptionService.getUnreadCount(user); + Map unreadCount = feedSubscriptionService.getUnreadCount(user); root = buildCategory(null, categories, subscriptions, unreadCount); root.setId("all"); @@ -397,7 +393,8 @@ public class CategoryREST extends AbstractREST { return Response.ok(root).build(); } - private Category buildCategory(Long id, List categories, List subscriptions, Map unreadCount) { + private Category buildCategory(Long id, List categories, List subscriptions, + Map unreadCount) { Category category = new Category(); category.setId(String.valueOf(id)); category.setExpanded(true); @@ -425,9 +422,8 @@ public class CategoryREST extends AbstractREST { for (FeedSubscription subscription : subscriptions) { if ((id == null && subscription.getCategory() == null) || (subscription.getCategory() != null && ObjectUtils.equals(subscription.getCategory().getId(), id))) { - Long size = unreadCount.get(subscription.getId()); - long unread = size == null ? 0 : size; - Subscription sub = Subscription.build(subscription, applicationSettingsService.get().getPublicUrl(), unread); + UnreadCount uc = unreadCount.get(subscription.getId()); + Subscription sub = Subscription.build(subscription, applicationSettingsService.get().getPublicUrl(), uc); category.getFeeds().add(sub); } } diff --git a/src/main/java/com/commafeed/frontend/rest/resources/FeedREST.java b/src/main/java/com/commafeed/frontend/rest/resources/FeedREST.java index a5be05ef..e703435b 100644 --- a/src/main/java/com/commafeed/frontend/rest/resources/FeedREST.java +++ b/src/main/java/com/commafeed/frontend/rest/resources/FeedREST.java @@ -64,6 +64,7 @@ import com.commafeed.frontend.model.Entries; import com.commafeed.frontend.model.Entry; import com.commafeed.frontend.model.FeedInfo; import com.commafeed.frontend.model.Subscription; +import com.commafeed.frontend.model.UnreadCount; import com.commafeed.frontend.model.request.FeedInfoRequest; import com.commafeed.frontend.model.request.FeedModificationRequest; import com.commafeed.frontend.model.request.IDRequest; @@ -146,7 +147,7 @@ public class FeedREST extends AbstractREST { Preconditions.checkNotNull(id); Preconditions.checkNotNull(readType); - + keywords = StringUtils.trimToNull(keywords); Preconditions.checkArgument(keywords == null || StringUtils.length(keywords) >= 3); @@ -317,10 +318,7 @@ public class FeedREST extends AbstractREST { if (sub == null) { return Response.status(Status.NOT_FOUND).build(); } - Long unreadCount = feedSubscriptionService.getUnreadCount(getUser()).get(id); - if (unreadCount == null) { - unreadCount = new Long(0); - } + UnreadCount unreadCount = feedSubscriptionService.getUnreadCount(getUser()).get(id); return Response.ok(Subscription.build(sub, applicationSettingsService.get().getPublicUrl(), unreadCount)).build(); }