Add Infrequent tab and corresponding user setting

This commit is contained in:
2026-03-17 21:38:17 -05:00
parent d05c5b9d7f
commit e2a1630adc
16 changed files with 126 additions and 6 deletions

View File

@@ -98,7 +98,7 @@ public class Feed extends AbstractModel {
private String etagHeader;
/**
* average time between entries in the feed
* average time between entries in the feed in milliseconds
*/
private Long averageEntryInterval;

View File

@@ -146,6 +146,8 @@ public class UserSettings extends AbstractModel {
private boolean unreadCountFavicon;
private boolean disablePullToRefresh;
private int infrequentThresholdDays;
private boolean email;
private boolean gmail;
private boolean facebook;

View File

@@ -76,6 +76,9 @@ public class Settings implements Serializable {
@Schema(description = "disable pull to refresh", required = true)
private boolean disablePullToRefresh;
@Schema(description = "threshold in days for the infrequent view", required = true)
private int infrequentThresholdDays;
@Schema(description = "primary theme color to use in the UI")
private String primaryColor;

View File

@@ -71,6 +71,9 @@ public class Subscription implements Serializable {
@Schema(description = "automatically mark entries as read after this many days (null to disable)")
private Integer autoMarkAsReadAfterDays;
@Schema(description = "average time in milliseconds between entries in this feed, null if unknown")
private Long averageEntryIntervalMs;
public static Subscription build(FeedSubscription subscription, UnreadCount unreadCount) {
FeedCategory category = subscription.getCategory();
Feed feed = subscription.getFeed();
@@ -93,6 +96,7 @@ public class Subscription implements Serializable {
sub.setFilterLegacy(subscription.getFilterLegacy());
sub.setPushNotificationsEnabled(subscription.isPushNotificationsEnabled());
sub.setAutoMarkAsReadAfterDays(subscription.getAutoMarkAsReadAfterDays());
sub.setAverageEntryIntervalMs(feed.getAverageEntryInterval());
return sub;
}

View File

@@ -40,12 +40,14 @@ import com.commafeed.CommaFeedConfiguration;
import com.commafeed.backend.dao.FeedCategoryDAO;
import com.commafeed.backend.dao.FeedEntryStatusDAO;
import com.commafeed.backend.dao.FeedSubscriptionDAO;
import com.commafeed.backend.dao.UserSettingsDAO;
import com.commafeed.backend.feed.FeedEntryKeyword;
import com.commafeed.backend.feed.FeedUtils;
import com.commafeed.backend.model.FeedCategory;
import com.commafeed.backend.model.FeedEntryStatus;
import com.commafeed.backend.model.FeedSubscription;
import com.commafeed.backend.model.User;
import com.commafeed.backend.model.UserSettings;
import com.commafeed.backend.model.UserSettings.ReadingMode;
import com.commafeed.backend.model.UserSettings.ReadingOrder;
import com.commafeed.backend.service.FeedEntryService;
@@ -83,6 +85,7 @@ public class CategoryREST {
public static final String ALL = "all";
public static final String STARRED = "starred";
public static final String INFREQUENT = "infrequent";
private final AuthenticationContext authenticationContext;
private final FeedCategoryDAO feedCategoryDAO;
@@ -90,6 +93,7 @@ public class CategoryREST {
private final FeedSubscriptionDAO feedSubscriptionDAO;
private final FeedEntryService feedEntryService;
private final FeedSubscriptionService feedSubscriptionService;
private final UserSettingsDAO userSettingsDAO;
private final CommaFeedConfiguration config;
private final UriInfo uri;
@@ -139,11 +143,15 @@ public class CategoryREST {
}
User user = authenticationContext.getCurrentUser();
if (ALL.equals(id)) {
if (ALL.equals(id) || INFREQUENT.equals(id)) {
entries.setName(Optional.ofNullable(tag).orElse("All"));
List<FeedSubscription> subs = feedSubscriptionDAO.findAll(user);
removeExcludedSubscriptions(subs, excludedIds);
if (INFREQUENT.equals(id)) {
entries.setName("Infrequent");
removeFrequentSubscriptions(subs, user);
}
List<FeedEntryStatus> list = feedEntryStatusDAO.findBySubscriptions(user, subs, unreadOnly, entryKeywords, newerThanDate,
offset, limit + 1, order, true, tag, null, null);
@@ -244,9 +252,12 @@ public class CategoryREST {
List<FeedEntryKeyword> entryKeywords = FeedEntryKeyword.fromQueryString(keywords);
User user = authenticationContext.getCurrentUser();
if (ALL.equals(req.getId())) {
if (ALL.equals(req.getId()) || INFREQUENT.equals(req.getId())) {
List<FeedSubscription> subs = feedSubscriptionDAO.findAll(user);
removeExcludedSubscriptions(subs, req.getExcludedSubscriptions());
if (INFREQUENT.equals(req.getId())) {
removeFrequentSubscriptions(subs, user);
}
feedEntryService.markSubscriptionEntries(user, subs, olderThan, insertedBefore, entryKeywords);
} else if (STARRED.equals(req.getId())) {
feedEntryService.markStarredEntries(user, olderThan, insertedBefore);
@@ -260,6 +271,17 @@ public class CategoryREST {
return Response.ok().build();
}
private void removeFrequentSubscriptions(List<FeedSubscription> subs, User user) {
UserSettings userSettings = userSettingsDAO.findByUser(user);
int infrequentDays = userSettings != null && userSettings.getInfrequentThresholdDays() > 0
? userSettings.getInfrequentThresholdDays()
: 7;
long infrequentThresholdMs = (long) infrequentDays * 24 * 3600 * 1000;
subs.removeIf(
sub -> sub.getFeed().getAverageEntryInterval() == null || sub.getFeed().getAverageEntryInterval() < infrequentThresholdMs);
}
private void removeExcludedSubscriptions(List<FeedSubscription> subs, List<Long> excludedIds) {
if (CollectionUtils.isNotEmpty(excludedIds)) {
subs.removeIf(sub -> excludedIds.contains(sub.getId()));

View File

@@ -133,6 +133,7 @@ public class UserREST {
s.setUnreadCountFavicon(settings.isUnreadCountFavicon());
s.setDisablePullToRefresh(settings.isDisablePullToRefresh());
s.setPrimaryColor(settings.getPrimaryColor());
s.setInfrequentThresholdDays(settings.getInfrequentThresholdDays());
if (settings.getPushNotifications() != null) {
s.getPushNotificationSettings().setType(settings.getPushNotifications().getType());
@@ -168,6 +169,7 @@ public class UserREST {
s.setUnreadCountTitle(false);
s.setUnreadCountFavicon(true);
s.setDisablePullToRefresh(false);
s.setInfrequentThresholdDays(7);
}
return s;
}
@@ -205,6 +207,7 @@ public class UserREST {
s.setUnreadCountFavicon(settings.isUnreadCountFavicon());
s.setDisablePullToRefresh(settings.isDisablePullToRefresh());
s.setPrimaryColor(settings.getPrimaryColor());
s.setInfrequentThresholdDays(settings.getInfrequentThresholdDays());
PushNotificationUserSettings ps = new PushNotificationUserSettings();
ps.setType(settings.getPushNotificationSettings().getType());

View File

@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog https://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">
<changeSet id="add-infrequent-days-threshold" author="athou">
<addColumn tableName="USERSETTINGS">
<column name="infrequentThresholdDays" type="INT" valueNumeric="7">
<constraints nullable="false" />
</column>
</addColumn>
</changeSet>
</databaseChangeLog>

View File

@@ -38,5 +38,6 @@
<include file="changelogs/db.changelog-5.11.xml" />
<include file="changelogs/db.changelog-5.12.xml" />
<include file="changelogs/db.changelog-7.0.xml" />
<include file="changelogs/db.changelog-7.1.xml" />
</databaseChangeLog>