Working on feed caching & models.

This commit is contained in:
Max 2023-05-12 23:58:49 +02:00
parent fcda58d30f
commit 87f46e2178
14 changed files with 188 additions and 105 deletions

42
SharpRss/FeedCache.cs Normal file
View File

@ -0,0 +1,42 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using CodeHollow.FeedReader;
using Serilog;
using ToolQit.Extensions;
namespace SharpRss
{
/// <summary>
/// Global memory feed cache.
/// </summary>
public static class FeedCache
{
private static readonly Dictionary<string, Feed> CachedFeeds = new Dictionary<string, Feed>();
public static async Task<Feed> GetFeed(string urlKey)
{
Log.Verbose("Request for: {UrlKey}", urlKey);
if (urlKey.IsNullEmptyWhiteSpace())
{
Log.Error("RSS Url is empty!");
return new Feed();
}
if (CachedFeeds.TryGetValue(urlKey, out Feed? fModel))
return fModel;
string feedUrl;
var urls = await FeedReader.GetFeedUrlsFromUrlAsync(urlKey);
if (!urls.Any())
feedUrl = urlKey;
else
feedUrl = urls.First().Url;
Feed feed = await FeedReader.ReadAsync(feedUrl);
if (feed == null)
Log.Warning("Could not get feed: {FeedUrl}", feedUrl);
CachedFeeds.Add(urlKey, feed);
return feed;
}
}
}

View File

@ -1,15 +1,26 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Collections.Generic;
namespace SharpRss.Models
{
/// <summary>
/// To store and load data from file/database
/// </summary>
public class CategoryModel
public class CategoryModel : IGuideItem
{
public CategoryModel(CategoryModel model)
{
Name = model.Name;
Feeds = model.Feeds;
}
public CategoryModel(string name, HashSet<FeedModel> feeds)
{
Name = name;
Feeds = feeds;
}
public string Name { get; set; }
public HashSet<FeedModel> Feeds { get; set; }
public bool IsSelected { get; set; }
public bool IsExpanded { get; set; }
}
}

View File

@ -1,15 +1,43 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Dynamic;
using System.Linq;
using System.Threading.Tasks;
using CodeHollow.FeedReader;
using Serilog;
using ToolQit.Extensions;
namespace SharpRss.Models
{
public class FeedModel
public class FeedModel : IGuideItem
{
public string Type { get; set; }
public string Text { get; set; }
public string Title { get; set; }
public string XmlUrl { get; set; }
public string HtmlUrl { get; set; }
public FeedModel(string rssUrl)
{
_rssUrl = rssUrl;
Task.Run(async () => Base = await FeedCache.GetFeed(_rssUrl));
}
public Feed Base { get; private set; }
public bool IsSelected { get; set; }
public bool IsExpanded { get; set; }
public bool IsReady { get; private set; }
private readonly string _rssUrl;
/*public async void Load(bool reload = false)
{
if (Base != null && !reload || !IsReady) return;
if (_rssUrl.IsNullEmptyWhiteSpace())
{
Log.Error("RSS Url is empty!");
return;
}
IsReady = false;
string feedUrl;
var urls = await FeedReader.GetFeedUrlsFromUrlAsync(_rssUrl);
if (!urls.Any())
feedUrl = _rssUrl;
else
feedUrl = urls.First().Url;
Log.Verbose("Creating feed: {FeedUrl}", feedUrl);
Base = await FeedReader.ReadAsync(feedUrl);
IsReady = true;
}*/
}
}

View File

@ -1,6 +1,6 @@
namespace WebSharpRSS.Models
namespace SharpRss.Models
{
public interface ISelectableGuideItem
public interface IGuideItem
{
public bool IsSelected { get; set; }
public bool IsExpanded { get; set; }

View File

@ -19,9 +19,27 @@ namespace SharpRss
/// - Memory
}
private static HashSet<FeedModel> feedSet = new HashSet<FeedModel>()
{
new FeedModel("http://fedoramagazine.org/feed/"),
new FeedModel("https://www.nasa.gov/rss/dyn/breaking_news.rss")
};
private static HashSet<FeedModel> feedSet2 = new HashSet<FeedModel>()
{
new FeedModel("https://journals.plos.org/plosone/feed/atom"),
new FeedModel("https://cyberciti.biz/feed"),
new FeedModel("https://itsfoss.com/feed")
};
HashSet<CategoryModel> set = new HashSet<CategoryModel>()
{
new CategoryModel("RSS", feedSet),
new CategoryModel("Tech", feedSet2)
};
public async Task<HashSet<CategoryModel>> GetCategories()
{
return new HashSet<CategoryModel>();
return set;
}
public async Task<HashSet<FeedModel>> GetAllFeeds()

View File

@ -11,8 +11,15 @@ namespace WebSharpRSS
{
public static void SetAppDefaultSettings(this DataContainer dataCon)
{
dataCon.Set("FaviconResolveUrl", "https://icons.duckduckgo.com/ip3/{0}.ico", false);
dataCon.Set("LogPath", Path.Combine(Environment.CurrentDirectory, "logs", "log_.json"), false);
var paths = dataCon["Paths"];
paths.Set("FaviconResolveUrl", "https://icons.duckduckgo.com/ip3/{0}.ico", false);
paths.Set("LogPath", Path.Combine(Environment.CurrentDirectory, "logs", "log_.json"), false);
var dbSql = dataCon["SQL"];
dbSql.Set("Host", "localhost", false);
dbSql.Set("Port", "6969", false);
dbSql.Set("Username", "sharpUser", false);
dbSql.Set("Password", "sh@rP@s$", false);
}
private static LoggerConfiguration? _configuration;
@ -21,8 +28,9 @@ namespace WebSharpRSS
if (_configuration != null) return;
_configuration = new LoggerConfiguration()
.WriteTo.Console()
.WriteTo.File(new JsonFormatter(), Caretaker.Settings.GetString("LogPath"), rollingInterval: RollingInterval.Day)
.WriteTo.File(new JsonFormatter(), Caretaker.Settings["Paths"].GetString("LogPath"), rollingInterval: RollingInterval.Day)
.MinimumLevel.Verbose();
Log.Logger = _configuration.CreateLogger();
}
}

View File

@ -1,22 +0,0 @@
using System.Collections.Generic;
using CodeHollow.FeedReader;
namespace WebSharpRSS.Models
{
public class CategoryGuideItem : ISelectableGuideItem
{
public string CategoryTitle { get; set; }
public string CategoryIcon { get; set; }
private string _hexColor;
public string CategoryHexColor
{
get => _hexColor;
set => _hexColor = value.Insert(7, "80");
}
public bool IsExpanded { get; set; }
public bool IsSelected { get; set; }
public HashSet<FeedGuideItem> FeedItems { get; set; } = new HashSet<FeedGuideItem>() { new FeedGuideItem(FeedReader.ReadAsync("http://fedoramagazine.org/feed/").Result) };
}
}

View File

@ -1,15 +0,0 @@
using SharpRss.Models;
namespace WebSharpRSS.Models
{
public class CategoryItem : CategoryModel, ISelectableGuideItem
{
public CategoryItem(CategoryModel model)
{
Name = model.Name;
Feeds = model.Feeds;
}
public bool IsSelected { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
public bool IsExpanded { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
}
}

View File

@ -1,15 +0,0 @@
using CodeHollow.FeedReader;
namespace WebSharpRSS.Models
{
public class FeedGuideItem : ISelectableGuideItem
{
public FeedGuideItem(Feed feed)
{
Feed = feed;
}
public readonly Feed Feed;
public bool IsSelected { get; set; }
public bool IsExpanded { get; set; }
}
}

View File

@ -10,7 +10,7 @@ using WebSharpRSS;
Caretaker.Settings.SetAppDefaultSettings();
Bootstrapper.SetupLogging();
Log.Information("Starting app....");
Log.Information("Starting application....");
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
@ -18,7 +18,6 @@ builder.Services.AddServerSideBlazor();
builder.Services.AddTransient<RssService>();
builder.Services.AddMudServices(config =>
{
Log.Debug("Configuring MudServices");
config.SnackbarConfiguration.PositionClass = Defaults.Classes.Position.BottomRight;
config.SnackbarConfiguration.PreventDuplicates = true;
config.SnackbarConfiguration.ShowCloseIcon = true;

12
WebSharpRSS/Settings.json Normal file
View File

@ -0,0 +1,12 @@
{
"Paths": {
"FaviconResolveUrl": "https://icons.duckduckgo.com/ip3/{0}.ico",
"LogPath": "/home/max/GitHub/SharpRSS/WebSharpRSS/logs/log_.json"
},
"SQL": {
"Host": "localhost",
"Port": "6969",
"Username": "sharpUser",
"Password": "sh@rP@s$"
}
}

View File

@ -1,5 +1,8 @@
@using WebSharpRSS.Models
@using CodeHollow.FeedReader
@using SharpRss.Models
@using ToolQit
@using ToolQit.Containers
@using ToolQit.Extensions
<style>
.cat-item {
background: var(--background-color);
@ -61,28 +64,37 @@
<div>
<MudText>@HeaderText</MudText>
@foreach (CategoryGuideItem catItem in Categories)
@foreach (CategoryModel guideCategory in Categories)
{
<div>
<div @onclick="@(() => ItemClicked(catItem))" class="cat-item mud-ripple" style="--hover-bg-color: @catItem.CategoryHexColor; --background-color: @(catItem.IsSelected ? catItem.CategoryHexColor : "transparent")">
<div @onclick="@(() => ItemClicked(guideCategory))" class="cat-item mud-ripple" style="--hover-bg-color: @Colors.Blue.Accent1; --background-color: @(guideCategory.IsSelected ? Colors.Blue.Accent2 : "transparent")">
<div class="cat-item-icon">
<MudIcon Class="pointer-events-none" Icon="@catItem.CategoryIcon" Size="Size.Medium"/>
<MudIcon Class="pointer-events-none" Icon="@Icons.Material.Filled.RssFeed" Size="Size.Medium"/>
</div>
<div class="cat-item-text">
<MudText Class="pointer-events-none" Typo="Typo.subtitle1">@catItem.CategoryTitle</MudText>
<MudText Class="pointer-events-none" Typo="Typo.subtitle1">@guideCategory.Name</MudText>
</div>
</div>
@* Feeds *@
@if (catItem.IsExpanded && catItem.FeedItems != null)
@if (guideCategory.IsExpanded && guideCategory.Feeds != null)
{
foreach (FeedGuideItem feedItem in catItem.FeedItems)
foreach (FeedModel feed in guideCategory.Feeds)
{
<div @onclick="() => ItemClicked(feedItem)" class="feed-item mud-ripple" style="--hover-bg-color: @catItem.CategoryHexColor; --background-color: @(feedItem.IsSelected ? catItem.CategoryHexColor : "transparent")">
if (feed == null || feed.Base == null) continue;
<div @onclick="() => ItemClicked(feed)" class="feed-item mud-ripple" style="--hover-bg-color: @Colors.Blue.Accent1; --background-color: @(feed.IsSelected ? Colors.Blue.Accent2 : "transparent")">
<div class="feed-item-icon">
<MudImage ObjectFit="ObjectFit.Contain" Src="http://www.google.com/s2/favicons?domain=wikipedia.com"/>
@*@if (!guideFeed.FaviconUrl.IsNullEmptyWhiteSpace())
{
<MudImage ObjectFit="ObjectFit.ScaleDown" Src="@guideFeed.FaviconUrl"/>
}
else
{
<MudIcon Icon="@Icons.Material.Filled.RssFeed" Size="Size.Medium"/>
}*@
<MudIcon Icon="@Icons.Material.Filled.RssFeed" Size="Size.Medium"/>
</div>
<div class="feed-item-text">
<MudText Class="pointer-events-none">@feedItem.Feed.Title</MudText>
<MudText Class="pointer-events-none">@feed.Base.Title</MudText>
</div>
</div>
}
@ -95,31 +107,31 @@
[Parameter]
public string HeaderText { get; set; }
[Parameter]
public HashSet<CategoryGuideItem> Categories { get; set; } = new HashSet<CategoryGuideItem>();
public HashSet<CategoryModel> Categories { get; set; } = new HashSet<CategoryModel>();
[Parameter]
public Action<CategoryGuideItem>? CatItemClicked { get; set; }
public Action<CategoryModel>? CatItemClicked { get; set; }
[Parameter]
public Action<FeedGuideItem>? FeedItemClicked { get; set; }
public Action<FeedModel>? FeedItemClicked { get; set; }
IGuideItem? _selectedItem;
ISelectableGuideItem? _selectedCategory;
void ItemClicked(ISelectableGuideItem categoryItem)
void ItemClicked(IGuideItem categoryItem)
{
categoryItem.IsExpanded = !categoryItem.IsExpanded;
if (_selectedCategory != categoryItem)
if (_selectedItem != categoryItem)
{
if (_selectedCategory != null)
_selectedCategory.IsSelected = false;
_selectedCategory = categoryItem;
_selectedCategory.IsSelected = true;
if (_selectedItem != null)
_selectedItem.IsSelected = false;
_selectedItem = categoryItem;
_selectedItem.IsSelected = true;
}
switch (categoryItem)
{
case CategoryGuideItem catGuideItem:
case CategoryModel catGuideItem:
CatItemClicked?.Invoke(catGuideItem);
break;
case FeedGuideItem feedGuideItem:
case FeedModel feedGuideItem:
FeedItemClicked?.Invoke(feedGuideItem);
break;
}

View File

@ -1,5 +1,4 @@
@using SharpRss.Models;
@using WebSharpRSS.Models
@using MudBlazor.Utilities
@using CodeHollow.FeedReader
@using Serilog
@ -14,33 +13,34 @@
</MudStack>
@code {
public HashSet<CategoryGuideItem> Categories = new HashSet<CategoryGuideItem>();
public HashSet<CategoryItem> Cats = new HashSet<CategoryItem>();
public HashSet<CategoryModel> Categories = new HashSet<CategoryModel>();
protected override void OnInitialized()
{
Log.Verbose("Setting up test data");
Categories.Add(new CategoryGuideItem() { CategoryTitle = "Social", CategoryIcon = Icons.Material.Filled.People });
Categories = _rssService.GetCategories().Result;
/*Cats = _rssService.GetCategories().Result.Select(x => new GuideModel(x)).ToHashSet();*/
/*Categories.Add(new CategoryGuideItem() { CategoryTitle = "Social", CategoryIcon = Icons.Material.Filled.People });
Categories.Add(new CategoryGuideItem() { CategoryTitle = "Blogs", CategoryIcon = Icons.Material.Filled.RssFeed, CategoryHexColor = Colors.Green.Accent1 });
Categories.Add(new CategoryGuideItem() { CategoryTitle = "Tech", CategoryIcon = Icons.Material.Filled.Computer, CategoryHexColor = Colors.Brown.Lighten1 });
Categories.Add(new CategoryGuideItem() { CategoryTitle = "News", CategoryIcon = Icons.Material.Filled.Newspaper, CategoryHexColor = Colors.Red.Accent1 });
Categories.Add(new CategoryGuideItem() { CategoryTitle = "News", CategoryIcon = Icons.Material.Filled.Newspaper, CategoryHexColor = Colors.Red.Accent1 });*/
}
private void Callback(MudListItem obj)
{
switch (obj.Value)
{
case CategoryGuideItem catTreeItem:
case CategoryModel catModel:
break;
case Feed feed:
case FeedModel feedModel:
break;
}
}
private void CategoryClicked(CategoryGuideItem catItem)
private void CategoryClicked(CategoryModel cat)
{
}
private void FeedClicked(FeedGuideItem feedItem)
private void FeedClicked(FeedModel guideFeedItem)
{
}

View File

@ -15,4 +15,9 @@
<ProjectReference Include="..\SharpRss\SharpRss.csproj" />
</ItemGroup>
<ItemGroup>
<Folder Include="Core" />
<Folder Include="Models" />
</ItemGroup>
</Project>