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