mirror of
https://github.com/MarginaliaSearch/MarginaliaSearch.git
synced 2025-02-23 13:09:00 +00:00
(search) Add clustering to subscriptions view
This commit is contained in:
parent
2a3c63f209
commit
6d18e6d840
@ -13,11 +13,9 @@ import nu.marginalia.search.model.NavbarModel;
|
|||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.Comparator;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/** Renders the front page (index) */
|
/** Renders the front page (index) */
|
||||||
@Singleton
|
@Singleton
|
||||||
@ -46,7 +44,7 @@ public class SearchFrontPageService {
|
|||||||
@Path("/")
|
@Path("/")
|
||||||
public MapModelAndView render(Context context) {
|
public MapModelAndView render(Context context) {
|
||||||
|
|
||||||
List<NewsItem> newsItems = getNewsItems(context);
|
List<NewsItemCluster> newsItems = getNewsItems(context);
|
||||||
|
|
||||||
IndexModel model = new IndexModel(newsItems, searchVisitorCount.getQueriesPerMinute());
|
IndexModel model = new IndexModel(newsItems, searchVisitorCount.getQueriesPerMinute());
|
||||||
|
|
||||||
@ -56,7 +54,7 @@ public class SearchFrontPageService {
|
|||||||
.put("websiteUrl", websiteUrl);
|
.put("websiteUrl", websiteUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<NewsItem> getNewsItems(Context context) {
|
private List<NewsItemCluster> getNewsItems(Context context) {
|
||||||
|
|
||||||
Set<Integer> subscriptions = subscriptionService.getSubscriptions(context);
|
Set<Integer> subscriptions = subscriptionService.getSubscriptions(context);
|
||||||
|
|
||||||
@ -69,7 +67,8 @@ public class SearchFrontPageService {
|
|||||||
feedResults.add(feedsClient.getFeed(sub));
|
feedResults.add(feedsClient.getFeed(sub));
|
||||||
}
|
}
|
||||||
|
|
||||||
List<NewsItem> ret = new ArrayList<>();
|
List<NewsItem> itemsAll = new ArrayList<>();
|
||||||
|
|
||||||
for (var result : feedResults) {
|
for (var result : feedResults) {
|
||||||
try {
|
try {
|
||||||
RpcFeed feed = result.get();
|
RpcFeed feed = result.get();
|
||||||
@ -79,7 +78,10 @@ public class SearchFrontPageService {
|
|||||||
if (title.isBlank()) {
|
if (title.isBlank()) {
|
||||||
title = "[Missing Title]";
|
title = "[Missing Title]";
|
||||||
}
|
}
|
||||||
ret.add(new NewsItem(title, item.getUrl(), feed.getDomain(), item.getDescription(), item.getDate()));
|
|
||||||
|
itemsAll.add(
|
||||||
|
new NewsItem(title, item.getUrl(), feed.getDomain(), item.getDescription(), item.getDate())
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex) {
|
catch (Exception ex) {
|
||||||
@ -87,11 +89,29 @@ public class SearchFrontPageService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ret.sort(Comparator.comparing(NewsItem::date).reversed());
|
Map<String, List<NewsItem>> ret =
|
||||||
if (ret.size() > 25) {
|
itemsAll.stream()
|
||||||
ret.subList(25, ret.size()).clear();
|
.sorted(Comparator.comparing(NewsItem::date).reversed())
|
||||||
|
.collect(Collectors.groupingBy(NewsItem::domain));
|
||||||
|
|
||||||
|
List<NewsItemCluster> items = new ArrayList<>(ret.size());
|
||||||
|
|
||||||
|
for (var itemsForDomain : ret.values()) {
|
||||||
|
itemsForDomain.sort(
|
||||||
|
Comparator
|
||||||
|
.comparing(NewsItem::date)
|
||||||
|
.reversed()
|
||||||
|
);
|
||||||
|
items.add(new NewsItemCluster(itemsForDomain));
|
||||||
}
|
}
|
||||||
return ret;
|
|
||||||
|
items.sort(Comparator.comparing((NewsItemCluster item) -> item.first().date).reversed());
|
||||||
|
|
||||||
|
// No more than 20 news item clusters on the front page
|
||||||
|
if (items.size() > 20) {
|
||||||
|
items.subList(20, items.size()).clear();
|
||||||
|
}
|
||||||
|
return items;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -134,6 +154,19 @@ public class SearchFrontPageService {
|
|||||||
return sb.toString();
|
return sb.toString();
|
||||||
}*/
|
}*/
|
||||||
|
|
||||||
public record IndexModel(List<NewsItem> news, int searchPerMinute) { }
|
public record IndexModel(List<NewsItemCluster> news, int searchPerMinute) { }
|
||||||
public record NewsItem(String title, String url, String domain, String description, String date) {}
|
public record NewsItem(String title,
|
||||||
|
String url,
|
||||||
|
String domain,
|
||||||
|
String description,
|
||||||
|
String date
|
||||||
|
) {}
|
||||||
|
public record NewsItemCluster(
|
||||||
|
NewsItem first,
|
||||||
|
List<NewsItem> rest) {
|
||||||
|
|
||||||
|
public NewsItemCluster(List<NewsItem> items) {
|
||||||
|
this(items.getFirst(), items.subList(1, Math.min(5, items.size())));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
@import nu.marginalia.search.model.SearchProfile
|
@import nu.marginalia.search.model.SearchProfile
|
||||||
@import nu.marginalia.search.svc.SearchFrontPageService.IndexModel
|
@import nu.marginalia.search.svc.SearchFrontPageService.IndexModel
|
||||||
@import nu.marginalia.search.svc.SearchFrontPageService.NewsItem
|
@import nu.marginalia.search.svc.SearchFrontPageService.NewsItem
|
||||||
|
@import nu.marginalia.search.svc.SearchFrontPageService.NewsItemCluster
|
||||||
|
|
||||||
@param NavbarModel navbar
|
@param NavbarModel navbar
|
||||||
@param WebsiteUrl websiteUrl
|
@param WebsiteUrl websiteUrl
|
||||||
@ -81,21 +82,38 @@
|
|||||||
@else
|
@else
|
||||||
<div class="max-w-7xl mx-auto flex flex-col space-y-4 fill-w m-4">
|
<div class="max-w-7xl mx-auto flex flex-col space-y-4 fill-w m-4">
|
||||||
<div class="my-4 text-black dark:text-white font-serif text-xl mx-8">Subscriptions</div>
|
<div class="my-4 text-black dark:text-white font-serif text-xl mx-8">Subscriptions</div>
|
||||||
@for (NewsItem item : model.news())
|
@for (NewsItemCluster cluster : model.news())
|
||||||
<div class="border dark:border-gray-600 rounded bg-white dark:bg-gray-800 flex flex-col overflow-hidden mx-4 p-4 space-y-2">
|
!{NewsItem item = cluster.first();}
|
||||||
<div class="text-black dark:text-white flex place-items-middle">
|
|
||||||
<div class="flex flex-col flex-col space-y-1">
|
<div class="border dark:border-gray-600 rounded bg-white dark:bg-gray-800 flex flex-col overflow-hidden mx-4 space-y-2">
|
||||||
<a class="text-l text-liteblue dark:text-blue-200" href="${item.url()}" rel="ugc external nofollow">${item.title()}</a>
|
<a class="flex space-x-2 flex-row place-items-baseline bg-margeblue text-white p-2 text-md" href="/site/${item.domain()}">
|
||||||
<a class="text-xs text-gray-800 dark:text-gray-100" href="/site/${item.domain()}">
|
<i class="fas fa-globe mr-2"></i>
|
||||||
<i class="fas fa-globe"></i> ${item.domain()}
|
<span>${item.domain()}</span>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
|
<div class="text-black dark:text-white flex place-items-middle mx-2">
|
||||||
|
<div class="flex flex-col space-y-1">
|
||||||
|
<a class="text-l text-liteblue dark:text-blue-200 visited:text-purple-800 dark:visited:text-purple-200" href="${item.url()}" rel="ugc external nofollow">${item.title()}</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="grow"></div>
|
<div class="grow"></div>
|
||||||
<div class="flex text-xs">
|
<div class="flex text-xs whitespace-nowrap">
|
||||||
${item.date().substring(0, 10)}
|
${item.date().substring(0, 10)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="text-sm text-gray-800 dark:text-gray-100">$unsafe{item.description()}</div>
|
<div class="text-sm text-gray-800 dark:text-gray-100 mx-2">$unsafe{item.description()}</div>
|
||||||
|
|
||||||
|
@for (var remainder : cluster.rest())
|
||||||
|
<div class="flex flex-col space-y-1 dark:border-gray-600 border-t pt-2 mx-2">
|
||||||
|
<div class="text-black dark:text-white flex place-items-middle">
|
||||||
|
<a class="text-sm text-liteblue dark:text-blue-200 visited:text-purple-800 dark:visited:text-purple-200" href="${remainder.url()}" rel="ugc external nofollow">${remainder.title()}</a>
|
||||||
|
<div class="grow"></div>
|
||||||
|
<div class="flex text-xs whitespace-nowrap">
|
||||||
|
${remainder.date().substring(0, 10)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@endfor
|
||||||
|
<div class="pt-2"></div>
|
||||||
</div>
|
</div>
|
||||||
@endfor
|
@endfor
|
||||||
</div>
|
</div>
|
||||||
|
Loading…
Reference in New Issue
Block a user