Simplified UI & Started database backend implementations

This commit is contained in:
Max Holleman 2023-05-19 14:40:57 +02:00
parent 24d62d79dc
commit 43cb208910
9 changed files with 117 additions and 38 deletions

View File

@ -0,0 +1,32 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace SharpRss.Models
{
public class FeedItemModel
{
/// <summary>
/// Last time the item is fetched.
/// </summary>
public DateTime LastUpdated { get; set; }
/// <summary>
/// The feed in which the item is part.
/// </summary>
public string FeedId { get; set; }
/// <summary>
/// If the item is read.
/// </summary>
public string Read { get; set; }
public string Type { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public string Link { get; set; }
public DateTime? PublishingDate { get; set; }
public string Author { get; set; }
public string Id { get; set; }
public string[] Categories { get; set; }
public string Content { get; set; }
}
}

View File

@ -16,10 +16,16 @@ namespace SharpRss.Models
FeedId = Guid.NewGuid().ToString();
FeedUrl = rssFeedUrl;
}
public string FeedId { get; private set; }
public string CategoryId { get; set; } = "";
public string FeedType { get; set; }
public string FeedUrl { get; set; }
public string FeedId { get; private set; }
public string CategoryId { get; set; } = "";
public string Description { get; set; }
public string Language { get; set; }
public string Copyright { get; set; }
public DateTime LastUpdated { get; set; }
public string ImageUrl { get; set; }
public static FeedModel Create(string url, string feedId, string categoryId)
{

View File

@ -2,6 +2,7 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Dapper;
using Microsoft.Data.Sqlite;
@ -78,23 +79,24 @@ namespace SharpRss.Services
private async void InitializeDb()
{
Log.Verbose("Checking database...");
HashSet<string> failed = new HashSet<string>();
_sqlConn.Open();
// Check category_data table
var queryResponse = await _sqlConn.QueryAsync("CREATE TABLE IF NOT EXISTS category_data (name STRING NOT NULL, hex_color STRING NOT NULL, path_icon STRING, category_id STRING PRIMARY KEY, CONSTRAINT name UNIQUE (name))");
if (queryResponse.Any())
{
_sqlConn.Close();
_sqlConn.Dispose();
throw new SqliteException("Error initializing database!", 0);
}
queryResponse = await _sqlConn.QueryAsync("CREATE TABLE IF NOT EXISTS feed_data (url STRING NOT NULL, feed_id STRING PRIMARY KEY, category_id STRING NOT NULL DEFAULT '', CONSTRAINT url, UNIQUE (url))");
if (queryResponse.Any())
{
_sqlConn.Close();
_sqlConn.Dispose();
throw new SqliteException("Error initializing database!", 0);
}
if (queryResponse.Any()) failed.Add("category_data");
// Check feed_data table
queryResponse = await _sqlConn.QueryAsync("CREATE TABLE IF NOT EXISTS feed_data (url STRING NOT NULL, feed_id STRING PRIMARY KEY, category_id STRING NOT NULL DEFAULT '', CONSTRAINT url, UNIQUE (url))");
if (queryResponse.Any()) failed.Add("feed_data");
_sqlConn.Close();
Log.Verbose("Checking database done!");
if (failed.Any())
{
var joined = string.Join(',', failed);
Log.Error("Failed to initialize table(s): {TableNames}", joined);
}
else
Log.Verbose("Checking database done!");
}
public void Dispose()

View File

@ -10,7 +10,7 @@ namespace SharpRss.Services
/// <summary>
/// Managing RSS feeds and categories.
/// </summary>
public class RssService
public class RssService : IDisposable
{
public RssService()
{
@ -85,5 +85,10 @@ namespace SharpRss.Services
FeedModel feedModel = new FeedModel(rssUrl, category);
await _dbService.AddFeedsAsync(new HashSet<FeedModel>() { feedModel });
}
}
public void Dispose()
{
_dbService.Dispose();
}
}
}

View File

@ -2,6 +2,7 @@ using System;
using System.IO;
using Serilog;
using Serilog.Formatting.Json;
using Serilog.Sinks.SystemConsole.Themes;
using ToolQit;
using ToolQit.Containers;

View File

@ -0,0 +1,19 @@
using CodeHollow.FeedReader;
using SharpRss.Models;
namespace WebSharpRSS.Models
{
public class FeedItemData
{
public FeedItemData(FeedItem feedItem)
{
FeedItem = feedItem;
}
public FeedModel FeedModel { get; set; }
public FeedItem FeedItem { get; set; }
public string? Icon {get; set; }
public string? FaviconUrl { get; set; }
public string? CategoryColorHex { get; set; }
public bool Read { get; set; }
}
}

View File

@ -23,6 +23,7 @@ namespace WebSharpRSS.Models
{
_service = rssService;
FeedModel = feedModel;
Initialize();
}

View File

@ -4,27 +4,34 @@
@using WebSharpRSS.Models;
@using SharpRss.Services
@*@inject RssService _rssService;*@
@inject FeedStateContainer _stateContainer;
<MudGrid Spacing="3" Justify="Justify.FlexStart">
@foreach (var feedItem in _items)
<MudStack Spacing="2" Class="ml-2 mr-2">
@foreach (var feedItemData in _itemDatas)
{
<MudItem xs="6">
<MudItem>
<MudCard>
<MudCardContent>
<MudText>@feedItem.Title</MudText>
<MudText Typo="Typo.body2">@feedItem.Description</MudText>
<MudText Typo="Typo.overline">@feedItem.PublishingDate.ToString()</MudText>
<div style="justify-self: start;" class="d-flex align-center">
@if (feedItemData.Icon != null)
{
<MudIcon Icon="@feedItemData.Icon" Style="@($"color:{feedItemData.CategoryColorHex}")" />
}
@if (feedItemData.FaviconUrl != null)
{
<MudImage Src="@feedItemData.FaviconUrl" ObjectFit="ObjectFit.Contain" />
}
<MudText Class="d-inline pa-2 align-center">@feedItemData.FeedItem.Title</MudText>
</div>
<MudText Typo="Typo.body2">@feedItemData.FeedItem.Description</MudText>
<MudText Typo="Typo.overline">@feedItemData.FeedItem.PublishingDate.ToString()</MudText>
</MudCardContent>
</MudCard>
</MudItem>
}
</MudGrid>
</MudStack>
@code {
private HashSet<FeedItem> _items = new HashSet<FeedItem>();
protected override void OnInitialized()
{
UpdateFeeds();
@ -35,19 +42,25 @@
UpdateFeeds();
InvokeAsync(StateHasChanged);
}
private HashSet<FeedItemData> _itemDatas = new HashSet<FeedItemData>();
private TreeItemData? _treeItemData;
private void UpdateFeeds()
{
if (_stateContainer.TreeItem == null) return;
if (_stateContainer.TreeItem.Feed != null)
_items = _stateContainer.TreeItem.Feed.Items.ToHashSet();
if (_stateContainer.TreeItem.Feeds != null)
_treeItemData = _stateContainer.TreeItem;
if (_treeItemData.Feed != null)
{
_items = new HashSet<FeedItem>();
foreach (var itemData in _stateContainer.TreeItem.Feeds)
Feed feed = _treeItemData.Feed;
_itemDatas = feed.Items.Select(x => new FeedItemData(x) { Icon = _treeItemData.Icon, FaviconUrl = _treeItemData.FaviconUrl, CategoryColorHex = _treeItemData.CategoryModel?.HexColor }).ToHashSet();
}
else if (_treeItemData.Feeds != null)
{
HashSet<FeedItemData> items = new HashSet<FeedItemData>();
foreach (var treeItem in _treeItemData.Feeds)
{
if (itemData.Feed == null) continue;
_items.UnionWith(itemData.Feed.Items);
if (treeItem.Feed == null) continue;
items.UnionWith(treeItem.Feed.Items.Select(x => new FeedItemData(x) { Icon = treeItem.Icon, FaviconUrl = treeItem.FaviconUrl, CategoryColorHex = _treeItemData.CategoryModel?.HexColor }));
_itemDatas = items.OrderBy(x => x.FeedItem.PublishingDate).Reverse().ToHashSet();
}
}
}

View File

@ -63,6 +63,6 @@
_guideItems.UnionWith(items.Select(x => x is CategoryModel model ? new TreeItemData(model, _rssService) : x is FeedModel feedModel ? new TreeItemData(feedModel, _rssService) : throw new ArgumentException("Arg x is invalid!")));
StateHasChanged();
Log.Verbose(" Guide initialized!");
Log.Verbose("Guide initialized!");
}
}