From 43cb20891047725964addb867f389d5e1e02d70d Mon Sep 17 00:00:00 2001 From: Max Holleman Date: Fri, 19 May 2023 14:40:57 +0200 Subject: [PATCH] Simplified UI & Started database backend implementations --- SharpRss/Models/FeedItemModel.cs | 32 ++++++++++++++++++ SharpRss/Models/FeedModel.cs | 12 +++++-- SharpRss/Services/DatabaseService.cs | 30 +++++++++-------- SharpRss/Services/RssService.cs | 9 +++-- WebSharpRSS/Bootstrapper.cs | 1 + WebSharpRSS/Models/FeedItemData.cs | 19 +++++++++++ WebSharpRSS/Models/TreeItemData.cs | 1 + WebSharpRSS/Pages/Index.razor | 49 ++++++++++++++++++---------- WebSharpRSS/Shared/SideGuide.razor | 2 +- 9 files changed, 117 insertions(+), 38 deletions(-) create mode 100644 SharpRss/Models/FeedItemModel.cs create mode 100644 WebSharpRSS/Models/FeedItemData.cs diff --git a/SharpRss/Models/FeedItemModel.cs b/SharpRss/Models/FeedItemModel.cs new file mode 100644 index 0000000..f775acf --- /dev/null +++ b/SharpRss/Models/FeedItemModel.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace SharpRss.Models +{ + public class FeedItemModel + { + /// + /// Last time the item is fetched. + /// + public DateTime LastUpdated { get; set; } + /// + /// The feed in which the item is part. + /// + public string FeedId { get; set; } + /// + /// If the item is read. + /// + 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; } + } +} diff --git a/SharpRss/Models/FeedModel.cs b/SharpRss/Models/FeedModel.cs index 18d40c7..9dd33e3 100644 --- a/SharpRss/Models/FeedModel.cs +++ b/SharpRss/Models/FeedModel.cs @@ -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) { diff --git a/SharpRss/Services/DatabaseService.cs b/SharpRss/Services/DatabaseService.cs index 2b8f028..6238130 100644 --- a/SharpRss/Services/DatabaseService.cs +++ b/SharpRss/Services/DatabaseService.cs @@ -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 failed = new HashSet(); _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() diff --git a/SharpRss/Services/RssService.cs b/SharpRss/Services/RssService.cs index 4549552..32cb92b 100644 --- a/SharpRss/Services/RssService.cs +++ b/SharpRss/Services/RssService.cs @@ -10,7 +10,7 @@ namespace SharpRss.Services /// /// Managing RSS feeds and categories. /// - 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 }); } - } + + public void Dispose() + { + _dbService.Dispose(); + } + } } \ No newline at end of file diff --git a/WebSharpRSS/Bootstrapper.cs b/WebSharpRSS/Bootstrapper.cs index e0a18ed..86489e0 100644 --- a/WebSharpRSS/Bootstrapper.cs +++ b/WebSharpRSS/Bootstrapper.cs @@ -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; diff --git a/WebSharpRSS/Models/FeedItemData.cs b/WebSharpRSS/Models/FeedItemData.cs new file mode 100644 index 0000000..0e82b16 --- /dev/null +++ b/WebSharpRSS/Models/FeedItemData.cs @@ -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; } + } +} diff --git a/WebSharpRSS/Models/TreeItemData.cs b/WebSharpRSS/Models/TreeItemData.cs index 544ef19..b926644 100644 --- a/WebSharpRSS/Models/TreeItemData.cs +++ b/WebSharpRSS/Models/TreeItemData.cs @@ -23,6 +23,7 @@ namespace WebSharpRSS.Models { _service = rssService; FeedModel = feedModel; + Initialize(); } diff --git a/WebSharpRSS/Pages/Index.razor b/WebSharpRSS/Pages/Index.razor index 0bc5d24..dc6fb67 100644 --- a/WebSharpRSS/Pages/Index.razor +++ b/WebSharpRSS/Pages/Index.razor @@ -4,27 +4,34 @@ @using WebSharpRSS.Models; @using SharpRss.Services -@*@inject RssService _rssService;*@ @inject FeedStateContainer _stateContainer; - - @foreach (var feedItem in _items) + + @foreach (var feedItemData in _itemDatas) { - + - @feedItem.Title - @feedItem.Description - @feedItem.PublishingDate.ToString() +
+ @if (feedItemData.Icon != null) + { + + } + @if (feedItemData.FaviconUrl != null) + { + + } + @feedItemData.FeedItem.Title +
+ @feedItemData.FeedItem.Description + @feedItemData.FeedItem.PublishingDate.ToString()
} -
+ @code { - - private HashSet _items = new HashSet(); protected override void OnInitialized() { UpdateFeeds(); @@ -35,19 +42,25 @@ UpdateFeeds(); InvokeAsync(StateHasChanged); } - + private HashSet _itemDatas = new HashSet(); + 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(); - 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 items = new HashSet(); + 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(); } } } diff --git a/WebSharpRSS/Shared/SideGuide.razor b/WebSharpRSS/Shared/SideGuide.razor index 4ba6f56..687dafa 100644 --- a/WebSharpRSS/Shared/SideGuide.razor +++ b/WebSharpRSS/Shared/SideGuide.razor @@ -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!"); } } \ No newline at end of file