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.LoggerFactory;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/** Renders the front page (index) */
|
||||
@Singleton
|
||||
@ -46,7 +44,7 @@ public class SearchFrontPageService {
|
||||
@Path("/")
|
||||
public MapModelAndView render(Context context) {
|
||||
|
||||
List<NewsItem> newsItems = getNewsItems(context);
|
||||
List<NewsItemCluster> newsItems = getNewsItems(context);
|
||||
|
||||
IndexModel model = new IndexModel(newsItems, searchVisitorCount.getQueriesPerMinute());
|
||||
|
||||
@ -56,7 +54,7 @@ public class SearchFrontPageService {
|
||||
.put("websiteUrl", websiteUrl);
|
||||
}
|
||||
|
||||
private List<NewsItem> getNewsItems(Context context) {
|
||||
private List<NewsItemCluster> getNewsItems(Context context) {
|
||||
|
||||
Set<Integer> subscriptions = subscriptionService.getSubscriptions(context);
|
||||
|
||||
@ -69,7 +67,8 @@ public class SearchFrontPageService {
|
||||
feedResults.add(feedsClient.getFeed(sub));
|
||||
}
|
||||
|
||||
List<NewsItem> ret = new ArrayList<>();
|
||||
List<NewsItem> itemsAll = new ArrayList<>();
|
||||
|
||||
for (var result : feedResults) {
|
||||
try {
|
||||
RpcFeed feed = result.get();
|
||||
@ -79,7 +78,10 @@ public class SearchFrontPageService {
|
||||
if (title.isBlank()) {
|
||||
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) {
|
||||
@ -87,11 +89,29 @@ public class SearchFrontPageService {
|
||||
}
|
||||
}
|
||||
|
||||
ret.sort(Comparator.comparing(NewsItem::date).reversed());
|
||||
if (ret.size() > 25) {
|
||||
ret.subList(25, ret.size()).clear();
|
||||
Map<String, List<NewsItem>> ret =
|
||||
itemsAll.stream()
|
||||
.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();
|
||||
}*/
|
||||
|
||||
public record IndexModel(List<NewsItem> news, int searchPerMinute) { }
|
||||
public record NewsItem(String title, String url, String domain, String description, String date) {}
|
||||
public record IndexModel(List<NewsItemCluster> news, int searchPerMinute) { }
|
||||
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.svc.SearchFrontPageService.IndexModel
|
||||
@import nu.marginalia.search.svc.SearchFrontPageService.NewsItem
|
||||
@import nu.marginalia.search.svc.SearchFrontPageService.NewsItemCluster
|
||||
|
||||
@param NavbarModel navbar
|
||||
@param WebsiteUrl websiteUrl
|
||||
@ -81,21 +82,38 @@
|
||||
@else
|
||||
<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>
|
||||
@for (NewsItem item : 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">
|
||||
<div class="text-black dark:text-white flex place-items-middle">
|
||||
<div class="flex flex-col flex-col space-y-1">
|
||||
<a class="text-l text-liteblue dark:text-blue-200" href="${item.url()}" rel="ugc external nofollow">${item.title()}</a>
|
||||
<a class="text-xs text-gray-800 dark:text-gray-100" href="/site/${item.domain()}">
|
||||
<i class="fas fa-globe"></i> ${item.domain()}
|
||||
@for (NewsItemCluster cluster : model.news())
|
||||
!{NewsItem item = cluster.first();}
|
||||
|
||||
<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="flex space-x-2 flex-row place-items-baseline bg-margeblue text-white p-2 text-md" href="/site/${item.domain()}">
|
||||
<i class="fas fa-globe mr-2"></i>
|
||||
<span>${item.domain()}</span>
|
||||
</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 class="grow"></div>
|
||||
<div class="flex text-xs">
|
||||
<div class="flex text-xs whitespace-nowrap">
|
||||
${item.date().substring(0, 10)}
|
||||
</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>
|
||||
@endfor
|
||||
</div>
|
||||
|
Loading…
Reference in New Issue
Block a user