diff --git a/Application/App.xaml.cs b/Application/App.xaml.cs index ab1115f..3895b15 100644 --- a/Application/App.xaml.cs +++ b/Application/App.xaml.cs @@ -35,8 +35,8 @@ public partial class App var app = new App(); app.InitializeComponent(); - // Create an single instance handle to see if we are already running - var isolationHandle = SingleInstance.GetSingleInstanceHandleAsync(Name).Result; + // Create a single instance handle to see if we are already running + var isolationHandle = SingleInstance.GetSingleInstanceHandleAsync(Name).GetAwaiter().GetResult(); // If there is another copy then pass it the command line and exit if (isolationHandle == null) diff --git a/Application/Data/LegacyDatabase.cs b/Application/Data/LegacyDatabase.cs index 250bbdf..1d842b9 100644 --- a/Application/Data/LegacyDatabase.cs +++ b/Application/Data/LegacyDatabase.cs @@ -8,6 +8,7 @@ using System.Collections.Generic; using System.Data.SqlServerCe; using System.IO; using System.Linq; +using FeedCenter.Feeds; namespace FeedCenter.Data; diff --git a/Application/Entities.cs b/Application/Entities.cs index 6701322..2571e8e 100644 --- a/Application/Entities.cs +++ b/Application/Entities.cs @@ -3,6 +3,7 @@ using FeedCenter.Options; using Realms; using System; using System.Linq; +using FeedCenter.Feeds; namespace FeedCenter; diff --git a/Application/FeedCenter.csproj b/Application/FeedCenter.csproj index 71a0a75..c7dada7 100644 --- a/Application/FeedCenter.csproj +++ b/Application/FeedCenter.csproj @@ -1,6 +1,6 @@  - net9.0-windows8.0 + net10.0-windows8.0 WinExe false false @@ -56,6 +56,10 @@ + + + + diff --git a/Application/FeedCenter.csproj.DotSettings b/Application/FeedCenter.csproj.DotSettings index 688c02c..efe734a 100644 --- a/Application/FeedCenter.csproj.DotSettings +++ b/Application/FeedCenter.csproj.DotSettings @@ -1,3 +1,4 @@  - True + + False True \ No newline at end of file diff --git a/Application/FeedCenter.sln b/Application/FeedCenter.sln index fbd820a..0bc1575 100644 --- a/Application/FeedCenter.sln +++ b/Application/FeedCenter.sln @@ -12,6 +12,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution", "Solution", "{14 ..\README.md = ..\README.md EndProjectSection EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MinifluxClient", "..\..\MinifluxClient\Library\MinifluxClient.csproj", "{6A48937F-AA57-8512-DA42-95ED79CED6D3}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FeverClient", "..\..\FeverClient\FeverClient.csproj", "{8E502914-40F6-CD9C-769E-90B775D46088}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -39,6 +43,38 @@ Global {BD3D12F2-DE23-4466-83B1-1EB617A877A4}.Release|x64.ActiveCfg = Release|x86 {BD3D12F2-DE23-4466-83B1-1EB617A877A4}.Release|x86.ActiveCfg = Release|Any CPU {BD3D12F2-DE23-4466-83B1-1EB617A877A4}.Release|x86.Build.0 = Release|Any CPU + {6A48937F-AA57-8512-DA42-95ED79CED6D3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6A48937F-AA57-8512-DA42-95ED79CED6D3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6A48937F-AA57-8512-DA42-95ED79CED6D3}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {6A48937F-AA57-8512-DA42-95ED79CED6D3}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {6A48937F-AA57-8512-DA42-95ED79CED6D3}.Debug|x64.ActiveCfg = Debug|Any CPU + {6A48937F-AA57-8512-DA42-95ED79CED6D3}.Debug|x64.Build.0 = Debug|Any CPU + {6A48937F-AA57-8512-DA42-95ED79CED6D3}.Debug|x86.ActiveCfg = Debug|Any CPU + {6A48937F-AA57-8512-DA42-95ED79CED6D3}.Debug|x86.Build.0 = Debug|Any CPU + {6A48937F-AA57-8512-DA42-95ED79CED6D3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6A48937F-AA57-8512-DA42-95ED79CED6D3}.Release|Any CPU.Build.0 = Release|Any CPU + {6A48937F-AA57-8512-DA42-95ED79CED6D3}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {6A48937F-AA57-8512-DA42-95ED79CED6D3}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {6A48937F-AA57-8512-DA42-95ED79CED6D3}.Release|x64.ActiveCfg = Release|Any CPU + {6A48937F-AA57-8512-DA42-95ED79CED6D3}.Release|x64.Build.0 = Release|Any CPU + {6A48937F-AA57-8512-DA42-95ED79CED6D3}.Release|x86.ActiveCfg = Release|Any CPU + {6A48937F-AA57-8512-DA42-95ED79CED6D3}.Release|x86.Build.0 = Release|Any CPU + {8E502914-40F6-CD9C-769E-90B775D46088}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8E502914-40F6-CD9C-769E-90B775D46088}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8E502914-40F6-CD9C-769E-90B775D46088}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {8E502914-40F6-CD9C-769E-90B775D46088}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {8E502914-40F6-CD9C-769E-90B775D46088}.Debug|x64.ActiveCfg = Debug|Any CPU + {8E502914-40F6-CD9C-769E-90B775D46088}.Debug|x64.Build.0 = Debug|Any CPU + {8E502914-40F6-CD9C-769E-90B775D46088}.Debug|x86.ActiveCfg = Debug|Any CPU + {8E502914-40F6-CD9C-769E-90B775D46088}.Debug|x86.Build.0 = Debug|Any CPU + {8E502914-40F6-CD9C-769E-90B775D46088}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8E502914-40F6-CD9C-769E-90B775D46088}.Release|Any CPU.Build.0 = Release|Any CPU + {8E502914-40F6-CD9C-769E-90B775D46088}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {8E502914-40F6-CD9C-769E-90B775D46088}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {8E502914-40F6-CD9C-769E-90B775D46088}.Release|x64.ActiveCfg = Release|Any CPU + {8E502914-40F6-CD9C-769E-90B775D46088}.Release|x64.Build.0 = Release|Any CPU + {8E502914-40F6-CD9C-769E-90B775D46088}.Release|x86.ActiveCfg = Release|Any CPU + {8E502914-40F6-CD9C-769E-90B775D46088}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/Application/FeedCenter.sln.DotSettings b/Application/FeedCenter.sln.DotSettings index 8efd33d..744515f 100644 --- a/Application/FeedCenter.sln.DotSettings +++ b/Application/FeedCenter.sln.DotSettings @@ -4,4 +4,5 @@ True True True - True \ No newline at end of file + True + True \ No newline at end of file diff --git a/Application/FeedErrorWindow.xaml b/Application/FeedErrorWindow.xaml index e766709..ee9c15b 100644 --- a/Application/FeedErrorWindow.xaml +++ b/Application/FeedErrorWindow.xaml @@ -4,6 +4,7 @@ xmlns:linkControl="clr-namespace:ChrisKaczor.Wpf.Controls;assembly=ChrisKaczor.Wpf.Controls.Link" xmlns:controlBox="clr-namespace:ChrisKaczor.Wpf.Windows;assembly=ChrisKaczor.Wpf.Windows.ControlBox" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:feedCenter="clr-namespace:FeedCenter" + xmlns:feeds="clr-namespace:FeedCenter.Feeds" mc:Ignorable="d" x:Class="FeedCenter.FeedErrorWindow" Title="{x:Static my:Resources.FeedErrorWindow}" @@ -46,7 +47,7 @@ HeadersVisibility="Column" Background="{x:Null}" CanUserSortColumns="True" - d:DataContext="{d:DesignInstance Type=feedCenter:Feed}" SelectionChanged="FeedDataGrid_SelectionChanged"> + d:DataContext="{d:DesignInstance Type=feeds:Feed}" SelectionChanged="FeedDataGrid_SelectionChanged"> false, AccountType.GoogleReader => false, + AccountType.Miniflux => true, AccountType.Local => true, _ => throw new NotSupportedException() }; @@ -45,6 +47,7 @@ public class Account : RealmObject, INotifyDataErrorInfo { AccountType.Fever => false, AccountType.GoogleReader => false, + AccountType.Miniflux => true, AccountType.Local => true, _ => throw new NotSupportedException() }; @@ -158,21 +161,22 @@ public class Account : RealmObject, INotifyDataErrorInfo return new Account { Name = DefaultName, Type = AccountType.Local }; } - public int GetProgressSteps(FeedCenterEntities entities) + public async Task GetProgressSteps(Account account, AccountReadInput accountReadInput) { var progressSteps = Type switch { // Delegate to the right reader based on the account type - AccountType.Fever => new FeverReader().GetProgressSteps(entities), - AccountType.GoogleReader => new GoogleReaderReader().GetProgressSteps(entities), - AccountType.Local => new LocalReader().GetProgressSteps(entities), + AccountType.Fever => await new FeverReader(account).GetProgressSteps(accountReadInput), + AccountType.GoogleReader => await new GoogleReaderReader(account).GetProgressSteps(accountReadInput), + AccountType.Miniflux => await new MinifluxReader(account).GetProgressSteps(accountReadInput), + AccountType.Local => await new LocalReader(account).GetProgressSteps(accountReadInput), _ => throw new NotSupportedException() }; return progressSteps; } - public AccountReadResult Read(AccountReadInput accountReadInput) + public async Task Read(AccountReadInput accountReadInput) { // If not enabled then do nothing if (!Enabled) @@ -189,14 +193,25 @@ public class Account : RealmObject, INotifyDataErrorInfo return AccountReadResult.NotDue; } - var accountReadResult = Type switch + AccountReadResult accountReadResult; + switch (Type) { // Delegate to the right reader based on the account type - AccountType.Fever => new FeverReader().Read(this, accountReadInput), - AccountType.GoogleReader => new GoogleReaderReader().Read(this, accountReadInput), - AccountType.Local => new LocalReader().Read(this, accountReadInput), - _ => throw new NotSupportedException() - }; + case AccountType.Fever: + accountReadResult = await new FeverReader(this).Read(accountReadInput); + break; + case AccountType.GoogleReader: + accountReadResult = await new GoogleReaderReader(this).Read(accountReadInput); + break; + case AccountType.Miniflux: + accountReadResult = await new MinifluxReader(this).Read(accountReadInput); + break; + case AccountType.Local: + accountReadResult = await new LocalReader(this).Read(accountReadInput); + break; + default: + throw new NotSupportedException(); + } return accountReadResult; } diff --git a/Application/Feeds/AccountReadInput.cs b/Application/Feeds/AccountReadInput.cs index 8f3f627..3d1ebfe 100644 --- a/Application/Feeds/AccountReadInput.cs +++ b/Application/Feeds/AccountReadInput.cs @@ -1,6 +1,6 @@ using System; -namespace FeedCenter; +namespace FeedCenter.Feeds; public class AccountReadInput(FeedCenterEntities entities, Guid? feedId, bool forceRead, Action incrementProgress) { diff --git a/Application/Feeds/AccountReadResult.cs b/Application/Feeds/AccountReadResult.cs index 5ba9377..0ac9207 100644 --- a/Application/Feeds/AccountReadResult.cs +++ b/Application/Feeds/AccountReadResult.cs @@ -1,4 +1,4 @@ -namespace FeedCenter; +namespace FeedCenter.Feeds; public enum AccountReadResult { diff --git a/Application/Feeds/AccountType.cs b/Application/Feeds/AccountType.cs index 8589ab3..697c85c 100644 --- a/Application/Feeds/AccountType.cs +++ b/Application/Feeds/AccountType.cs @@ -1,8 +1,9 @@ -namespace FeedCenter; +namespace FeedCenter.Feeds; public enum AccountType { Local, Fever, - GoogleReader + GoogleReader, + Miniflux } \ No newline at end of file diff --git a/Application/Feeds/Category.cs b/Application/Feeds/Category.cs index c5e2f0e..b617bd4 100644 --- a/Application/Feeds/Category.cs +++ b/Application/Feeds/Category.cs @@ -1,11 +1,11 @@ -using JetBrains.Annotations; -using Realms; -using System; +using System; using System.Collections; using System.ComponentModel; using System.Linq; +using JetBrains.Annotations; +using Realms; -namespace FeedCenter; +namespace FeedCenter.Feeds; public class Category : RealmObject, INotifyDataErrorInfo { diff --git a/Application/Feeds/DataErrorDictionary.cs b/Application/Feeds/DataErrorDictionary.cs index d7c516a..8ac99f5 100644 --- a/Application/Feeds/DataErrorDictionary.cs +++ b/Application/Feeds/DataErrorDictionary.cs @@ -3,7 +3,7 @@ using System.Collections; using System.Collections.Generic; using System.ComponentModel; -namespace FeedCenter; +namespace FeedCenter.Feeds; internal class DataErrorDictionary : Dictionary> { diff --git a/Application/Feeds/Feed.cs b/Application/Feeds/Feed.cs index dbb2e36..f7b345f 100644 --- a/Application/Feeds/Feed.cs +++ b/Application/Feeds/Feed.cs @@ -19,7 +19,7 @@ using JetBrains.Annotations; using Realms; using Serilog; -namespace FeedCenter; +namespace FeedCenter.Feeds; public partial class Feed : RealmObject, INotifyDataErrorInfo { diff --git a/Application/Feeds/FeedItem.cs b/Application/Feeds/FeedItem.cs index 5b09604..40affe0 100644 --- a/Application/Feeds/FeedItem.cs +++ b/Application/Feeds/FeedItem.cs @@ -1,12 +1,12 @@ -using FeedCenter.Options; -using Realms; -using System; +using System; using System.Diagnostics; using System.Linq; using System.Text.RegularExpressions; using System.Threading.Tasks; +using FeedCenter.Options; +using Realms; -namespace FeedCenter; +namespace FeedCenter.Feeds; public partial class FeedItem : RealmObject { @@ -90,7 +90,12 @@ public partial class FeedItem : RealmObject { case AccountType.Fever: // Delegate to the right reader based on the account type - await FeverReader.MarkFeedItemRead(feed.Account, RemoteId); + await new FeverReader(feed.Account).MarkFeedItemRead(RemoteId); + + break; + case AccountType.Miniflux: + // Delegate to the right reader based on the account type + await new MinifluxReader(feed.Account).MarkFeedItemRead(RemoteId); break; case AccountType.Local: diff --git a/Application/Feeds/FeedReadResult.cs b/Application/Feeds/FeedReadResult.cs index 592a35d..e0bbab3 100644 --- a/Application/Feeds/FeedReadResult.cs +++ b/Application/Feeds/FeedReadResult.cs @@ -1,4 +1,4 @@ -namespace FeedCenter; +namespace FeedCenter.Feeds; public enum FeedReadResult { diff --git a/Application/Feeds/FeedType.cs b/Application/Feeds/FeedType.cs index 953594d..7f0a912 100644 --- a/Application/Feeds/FeedType.cs +++ b/Application/Feeds/FeedType.cs @@ -1,4 +1,4 @@ -namespace FeedCenter; +namespace FeedCenter.Feeds; public enum FeedType { diff --git a/Application/Feeds/FeverReader.cs b/Application/Feeds/FeverReader.cs index 891e74f..1101334 100644 --- a/Application/Feeds/FeverReader.cs +++ b/Application/Feeds/FeverReader.cs @@ -1,20 +1,26 @@ -using System; +using ChrisKaczor.FeverClient; +using System; using System.Linq; using System.Security.Cryptography; using System.Text; using System.Threading.Tasks; -using ChrisKaczor.FeverClient; -namespace FeedCenter; +namespace FeedCenter.Feeds; -internal class FeverReader : IAccountReader +internal class FeverReader(Account account) : IAccountReader { - public int GetProgressSteps(FeedCenterEntities entities) + public async Task GetProgressSteps(AccountReadInput accountReadInput) { - return 7; + var apiKey = account.Authenticate ? GetApiKey(account) : string.Empty; + + var feverClient = new FeverClient(account.Url, apiKey); + + var feeds = await feverClient.GetFeeds(); + + return feeds.Count() * 2 + 5; } - public AccountReadResult Read(Account account, AccountReadInput accountReadInput) + public async Task Read(AccountReadInput accountReadInput) { var checkTime = DateTimeOffset.UtcNow; @@ -24,11 +30,11 @@ internal class FeverReader : IAccountReader accountReadInput.IncrementProgress(); - var feverFeeds = feverClient.GetFeeds().Result.ToList(); + var feverFeeds = (await feverClient.GetFeeds()).ToList(); accountReadInput.IncrementProgress(); - var allFeverFeedItems = feverClient.GetAllFeedItems().Result.ToList(); + var allFeverFeedItems = (await feverClient.GetAllFeedItems()).ToList(); accountReadInput.IncrementProgress(); @@ -120,14 +126,14 @@ internal class FeverReader : IAccountReader account.LastChecked = checkTime; - transaction.Commit(); + await transaction.CommitAsync(); accountReadInput.IncrementProgress(); return AccountReadResult.Success; } - public static async Task MarkFeedItemRead(Account account, string feedItemId) + public async Task MarkFeedItemRead(string feedItemId) { var apiKey = account.Authenticate ? GetApiKey(account) : string.Empty; diff --git a/Application/Feeds/GoogleReaderReader.cs b/Application/Feeds/GoogleReaderReader.cs index 86e739c..f7d209d 100644 --- a/Application/Feeds/GoogleReaderReader.cs +++ b/Application/Feeds/GoogleReaderReader.cs @@ -1,19 +1,16 @@ using System; -using System.Linq; -using System.Security.Cryptography; -using System.Text; using System.Threading.Tasks; -namespace FeedCenter; +namespace FeedCenter.Feeds; -internal class GoogleReaderReader : IAccountReader +internal class GoogleReaderReader(Account account) : IAccountReader { - public int GetProgressSteps(FeedCenterEntities entities) + public Task GetProgressSteps(AccountReadInput accountReadInput) { - return 7; + return Task.FromResult(7); } - public AccountReadResult Read(Account account, AccountReadInput accountReadInput) + public async Task Read(AccountReadInput accountReadInput) { var checkTime = DateTimeOffset.UtcNow; @@ -119,20 +116,22 @@ internal class GoogleReaderReader : IAccountReader account.LastChecked = checkTime; - transaction.Commit(); + await transaction.CommitAsync(); accountReadInput.IncrementProgress(); return AccountReadResult.Success; } - public static async Task MarkFeedItemRead(Account account, string feedItemId) + public Task MarkFeedItemRead(string feedItemId) { //var apiKey = account.Authenticate ? GetApiKey(account) : string.Empty; //var feverClient = new FeverClient.FeverClient(account.Url, apiKey); //await feverClient.MarkFeedItemAsRead(int.Parse(feedItemId)); + + return Task.CompletedTask; } //private static string GetApiKey(Account account) diff --git a/Application/Feeds/IAccountReader.cs b/Application/Feeds/IAccountReader.cs index 090a0b8..9cb7e1a 100644 --- a/Application/Feeds/IAccountReader.cs +++ b/Application/Feeds/IAccountReader.cs @@ -1,7 +1,10 @@ -namespace FeedCenter; +using System.Threading.Tasks; + +namespace FeedCenter.Feeds; public interface IAccountReader { - public int GetProgressSteps(FeedCenterEntities entities); - public AccountReadResult Read(Account account, AccountReadInput accountReadInput); + public Task GetProgressSteps(AccountReadInput accountReadInput); + public Task Read(AccountReadInput accountReadInput); + public Task MarkFeedItemRead(string feedItemId); } \ No newline at end of file diff --git a/Application/Feeds/LocalReader.cs b/Application/Feeds/LocalReader.cs index acd3ed6..3096f99 100644 --- a/Application/Feeds/LocalReader.cs +++ b/Application/Feeds/LocalReader.cs @@ -1,19 +1,20 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Threading.Tasks; -namespace FeedCenter; +namespace FeedCenter.Feeds; -public class LocalReader : IAccountReader +public class LocalReader(Account account) : IAccountReader { - public int GetProgressSteps(FeedCenterEntities entities) + public Task GetProgressSteps(AccountReadInput accountReadInput) { - var enabledFeedCount = entities.Feeds.Count(f => f.Account.Type == AccountType.Local && f.Enabled); + var enabledFeedCount = accountReadInput.Entities.Feeds.Count(f => f.Account.Type == AccountType.Local && f.Enabled); - return enabledFeedCount; + return Task.FromResult(enabledFeedCount); } - public AccountReadResult Read(Account account, AccountReadInput accountReadInput) + public Task Read(AccountReadInput accountReadInput) { var checkTime = DateTimeOffset.UtcNow; @@ -37,6 +38,11 @@ public class LocalReader : IAccountReader accountReadInput.Entities.SaveChanges(() => account.LastChecked = checkTime); - return AccountReadResult.Success; + return Task.FromResult(AccountReadResult.Success); + } + + public Task MarkFeedItemRead(string feedItemId) + { + throw new NotImplementedException(); } } \ No newline at end of file diff --git a/Application/Feeds/MinifluxReader.cs b/Application/Feeds/MinifluxReader.cs new file mode 100644 index 0000000..9f30f80 --- /dev/null +++ b/Application/Feeds/MinifluxReader.cs @@ -0,0 +1,146 @@ +using System; +using System.Linq; +using System.Threading.Tasks; +using ChrisKaczor.MinifluxClient; + +namespace FeedCenter.Feeds; + +internal class MinifluxReader(Account account) : IAccountReader +{ + public async Task GetProgressSteps(AccountReadInput accountReadInput) + { + var minifluxClient = new MinifluxClient(account.Url, account.Password); + + int feedCount; + if (accountReadInput.FeedId.HasValue) + { + feedCount = 1; + } + else + { + var feeds = await minifluxClient.GetFeeds(); + feedCount = feeds.Count(); + } + + return feedCount * 2 + 4; + } + + public async Task Read(AccountReadInput accountReadInput) + { + var checkTime = DateTimeOffset.UtcNow; + + var minifluxClient = new MinifluxClient(account.Url, account.Password); + + accountReadInput.IncrementProgress(); + + var localFeeds = accountReadInput.Entities.Feeds.ToList(); + var remoteFeeds = (await minifluxClient.GetFeeds()).ToList(); + + if (accountReadInput.FeedId.HasValue) + { + localFeeds = localFeeds.Where(f => f.Id == accountReadInput.FeedId.Value).ToList(); + remoteFeeds = remoteFeeds.Where(rf => rf.Id.ToString() == localFeeds.First().RemoteId).ToList(); + } + + accountReadInput.IncrementProgress(); + + var transaction = accountReadInput.Entities.BeginTransaction(); + + foreach (var remoteFeed in remoteFeeds) + { + var feed = accountReadInput.Entities.Feeds.FirstOrDefault(f => f.RemoteId == remoteFeed.Id.ToString() && f.Account.Id == account.Id); + + if (feed == null) + { + feed = new Feed + { + Id = Guid.NewGuid(), + RemoteId = remoteFeed.Id.ToString(), + Title = remoteFeed.Title, + Source = remoteFeed.FeedUrl, + Link = remoteFeed.SiteUrl, + Account = account, + Name = remoteFeed.Title, + CategoryId = accountReadInput.Entities.DefaultCategory.Id, + Enabled = true, + CheckInterval = 0, + }; + + accountReadInput.Entities.Feeds.Add(feed); + } + + feed.Name = remoteFeed.Title; + feed.Title = remoteFeed.Title; + feed.Link = remoteFeed.SiteUrl; + feed.Source = remoteFeed.FeedUrl; + feed.LastReadResult = FeedReadResult.Success; + feed.LastChecked = checkTime; + + accountReadInput.IncrementProgress(); + + var sequence = 1; + + var remoteFeedItems = (await minifluxClient.GetFeedEntries(remoteFeed.Id, SortField.PublishedAt, SortDirection.Descending)).ToList(); + + foreach (var remoteFeedItem in remoteFeedItems) + { + var feedItem = feed.Items.FirstOrDefault(f => f.RemoteId == remoteFeedItem.Id.ToString()); + + if (feedItem == null) + { + feedItem = new FeedItem + { + Id = Guid.NewGuid(), + RemoteId = remoteFeedItem.Id.ToString(), + Title = remoteFeedItem.Title, + Link = remoteFeedItem.Url, + Description = remoteFeedItem.Content, + BeenRead = remoteFeedItem.Status == "read", + FeedId = feed.Id, + Guid = Guid.NewGuid().ToString(), + Sequence = sequence++, + }; + + feed.Items.Add(feedItem); + } + + feedItem.LastFound = checkTime; + feedItem.BeenRead = remoteFeedItem.Status == "read"; + feedItem.Sequence = sequence++; + } + + accountReadInput.IncrementProgress(); + + var feedItemsNotSeen = feed.Items.Where(fi => fi.LastFound != checkTime).ToList(); + + foreach (var feedItemNotSeen in feedItemsNotSeen) + { + feed.Items.Remove(feedItemNotSeen); + } + } + + accountReadInput.IncrementProgress(); + + var feedsNotSeen = localFeeds.Where(f => f.Account.Id == account.Id && f.LastChecked != checkTime).ToList(); + + foreach (var feedNotSeen in feedsNotSeen) + { + accountReadInput.Entities.Feeds.Remove(feedNotSeen); + } + + account.LastChecked = checkTime; + + await transaction.CommitAsync(); + + accountReadInput.IncrementProgress(); + + return AccountReadResult.Success; + } + + public async Task MarkFeedItemRead(string feedItemId) + { + var minifluxClient = new MinifluxClient(account.Url, account.Password); + + await minifluxClient.MarkFeedEntries([long.Parse(feedItemId)], FeedEntryStatus.Read); + } +} \ No newline at end of file diff --git a/Application/Feeds/MultipleOpenAction.cs b/Application/Feeds/MultipleOpenAction.cs index fea3868..c146b85 100644 --- a/Application/Feeds/MultipleOpenAction.cs +++ b/Application/Feeds/MultipleOpenAction.cs @@ -1,4 +1,4 @@ -namespace FeedCenter; +namespace FeedCenter.Feeds; public enum MultipleOpenAction { diff --git a/Application/MainWindow/CategoryList.cs b/Application/MainWindow/CategoryList.cs index 881243d..1b9e1cf 100644 --- a/Application/MainWindow/CategoryList.cs +++ b/Application/MainWindow/CategoryList.cs @@ -3,6 +3,7 @@ using System; using System.Linq; using System.Windows; using System.Windows.Controls; +using FeedCenter.Feeds; namespace FeedCenter; diff --git a/Application/MainWindow/FeedCreation.cs b/Application/MainWindow/FeedCreation.cs index 4042520..0c5aef4 100644 --- a/Application/MainWindow/FeedCreation.cs +++ b/Application/MainWindow/FeedCreation.cs @@ -3,6 +3,7 @@ using System; using System.Linq; using System.Net; using System.Threading; +using FeedCenter.Feeds; namespace FeedCenter; diff --git a/Application/MainWindow/FeedList.cs b/Application/MainWindow/FeedList.cs index 71a6d16..9419734 100644 --- a/Application/MainWindow/FeedList.cs +++ b/Application/MainWindow/FeedList.cs @@ -38,7 +38,7 @@ public partial class MainWindow return; // Get the feed item - var feedItem = (FeedItem) ((ListBoxItem) sender).DataContext; + var feedItem = (Feeds.FeedItem) ((ListBoxItem) sender).DataContext; // Remove the item from the list LinkTextList.Items.Remove(feedItem); @@ -50,7 +50,7 @@ public partial class MainWindow private async void HandleItemMouseDoubleClick(object sender, MouseButtonEventArgs e) { // Get the feed item - var feedItem = (FeedItem) ((ListBoxItem) sender).DataContext; + var feedItem = (Feeds.FeedItem) ((ListBoxItem) sender).DataContext; // Try to open the item link if (!InstalledBrowser.OpenLink(Settings.Default.Browser, feedItem.Link)) @@ -104,7 +104,7 @@ public partial class MainWindow var menuItem = (MenuItem) sender; // Get the feed from the menu item tab - var feed = (Feed) menuItem.Tag; + var feed = (Feeds.Feed) menuItem.Tag; // Loop over all feeds and look for the index of the new one var feedIndex = 0; diff --git a/Application/MainWindow/FeedReading.cs b/Application/MainWindow/FeedReading.cs index 6d24fea..2d2934c 100644 --- a/Application/MainWindow/FeedReading.cs +++ b/Application/MainWindow/FeedReading.cs @@ -1,38 +1,19 @@ using ChrisKaczor.ApplicationUpdate; +using FeedCenter.Feeds; using FeedCenter.Properties; using System; using System.Collections.Generic; -using System.ComponentModel; +using System.Diagnostics; using System.Linq; using System.Threading; +using System.Threading.Tasks; using System.Windows; namespace FeedCenter; public partial class MainWindow { - private BackgroundWorker _feedReadWorker; - - private class FeedReadWorkerInput - { - public bool ForceRead { get; } - public Guid? FeedId { get; } - - public FeedReadWorkerInput() - { - } - - public FeedReadWorkerInput(bool forceRead) - { - ForceRead = forceRead; - } - - public FeedReadWorkerInput(bool forceRead, Guid? feedId) - { - ForceRead = forceRead; - FeedId = feedId; - } - } + private bool _reading; private void SetProgressMode(bool showProgress, int maximum) { @@ -49,55 +30,72 @@ public partial class MainWindow } } - private void ReadCurrentFeed(bool forceRead = false) + private async void ReadCurrentFeed(bool forceRead = false) { - // Don't read if we're already working - if (_feedReadWorker.IsBusy) - return; + try + { + // Don't read if we're already working + if (_reading) + return; - // Don't read if there is nothing to read - if (!_database.Feeds.Any()) - return; + _reading = true; - // Switch to progress mode - SetProgressMode(true, 1); + // Don't read if there is nothing to read + if (!_database.Feeds.Any()) + return; - // Create the input class - var workerInput = new FeedReadWorkerInput(forceRead, _currentFeed.Id); + var accountReadInput = new AccountReadInput(_database, _currentFeed.Id, forceRead, () => { }); - // Start the worker - _feedReadWorker.RunWorkerAsync(workerInput); + // Switch to progress mode + SetProgressMode(true, await _currentFeed.Account.GetProgressSteps(_currentFeed.Account, accountReadInput)); + + // Start reading + await HandleFeedReadWorkerStart(forceRead, _currentFeed.Id); + } + catch (Exception exception) + { + HandleException(exception); + } } - private void ReadFeeds(bool forceRead = false) + private async Task ReadFeeds(bool forceRead = false) { // Don't read if we're already working - if (_feedReadWorker.IsBusy) + if (_reading) return; + _reading = true; + // Don't read if there is nothing to read if (!_database.Accounts.Any()) return; - var progressSteps = _database.Accounts.Sum(a => a.GetProgressSteps(_database)); + var accountReadInput = new AccountReadInput(_database, null, forceRead, () => { }); + + // Calculate total progress steps + var progressSteps = 0; + + foreach (var account in _database.Accounts) + { + progressSteps += await account.GetProgressSteps(account, accountReadInput); + } // Switch to progress mode SetProgressMode(true, progressSteps); - // Create the input class - var workerInput = new FeedReadWorkerInput(forceRead); - - // Start the worker - _feedReadWorker.RunWorkerAsync(workerInput); + // Start reading + await HandleFeedReadWorkerStart(forceRead, null); } - private void HandleFeedReadWorkerProgressChanged(object sender, ProgressChangedEventArgs e) + private void IncrementProgress() { + Debug.Assert(FeedReadProgress.Value + 1 <= FeedReadProgress.Maximum); + // Set progress - FeedReadProgress.Value = e.ProgressPercentage; + FeedReadProgress.Value++; } - private void HandleFeedReadWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) + private void CompleteRead() { // Refresh the database to current settings ResetDatabase(); @@ -119,6 +117,8 @@ public partial class MainWindow NewVersionLink.Visibility = Visibility.Visible; UpdateErrorLink(); + + _reading = false; } private void UpdateErrorLink() @@ -134,98 +134,61 @@ public partial class MainWindow : string.Format(Properties.Resources.FeedErrorsLink, feedErrorCount); } - private static void HandleFeedReadWorkerStart(object sender, DoWorkEventArgs e) + private async Task HandleFeedReadWorkerStart(bool forceRead, Guid? feedId) { - // Create a new database instance for just this thread - var database = new FeedCenterEntities(); - - // Get the worker - var worker = (BackgroundWorker)sender; - - // Get the input information - var workerInput = (FeedReadWorkerInput)e.Argument ?? new FeedReadWorkerInput(); - - // Setup for progress - var currentProgress = 0; - - var accountsToRead = new List(); - - // If we have a single feed then get the account for that feed - if (workerInput.FeedId != null) + try { - var feed = database.Feeds.FirstOrDefault(f => f.Id == workerInput.FeedId); + // Create a new database instance for just this thread + var database = new FeedCenterEntities(); - if (feed != null) - accountsToRead.Add(feed.Account); - } - else - { - // Otherwise get all accounts - accountsToRead.AddRange(database.Accounts); - } + var accountsToRead = new List(); - var incrementProgress = () => - { - // Increment progress - currentProgress += 1; + // If we have a single feed then get the account for that feed + if (feedId != null) + { + var feed = database.Feeds.FirstOrDefault(f => f.Id == feedId); + + if (feed != null) + accountsToRead.Add(feed.Account); + } + else + { + // Otherwise get all accounts + accountsToRead.AddRange(database.Accounts); + } + + // Loop over each account and read it + foreach (var account in accountsToRead) + { + var accountReadInput = new AccountReadInput(database, feedId, forceRead, IncrementProgress); + + await account.Read(accountReadInput); + } // Report progress - worker.ReportProgress(currentProgress); - }; + IncrementProgress(); - // Loop over each account and read it - foreach (var account in accountsToRead) - { - var accountReadInput = new AccountReadInput(database, workerInput.FeedId, workerInput.ForceRead, incrementProgress); + // See if we're due for a version check + if (UpdateCheck.LocalVersion.Major > 0 && DateTime.Now - Settings.Default.LastVersionCheck >= Settings.Default.VersionCheckInterval) + { + // Get the update information + await UpdateCheck.CheckForUpdate(Settings.Default.IncludePrerelease); - account.Read(accountReadInput); + // Update the last check time + Settings.Default.LastVersionCheck = DateTime.Now; + } + + // Report progress + IncrementProgress(); + + // Sleep for a little bit so the user can see the update + Thread.Sleep(Settings.Default.ProgressSleepInterval * 3); + + CompleteRead(); } - - //// Create the list of feeds to read - //var feedsToRead = new List(); - - //// If we have a single feed then add it to the list - otherwise add them all - //if (workerInput.FeedId != null) - // feedsToRead.Add(database.Feeds.First(feed => feed.Id == workerInput.FeedId)); - //else - // feedsToRead.AddRange(database.Feeds); - - //// Loop over each feed and read it - //foreach (var feed in feedsToRead) - //{ - // // Read the feed - // database.SaveChanges(() => feed.Read(workerInput.ForceRead)); - - // // Increment progress - // currentProgress += 1; - - // // Report progress - // worker.ReportProgress(currentProgress); - //} - - // Increment progress - currentProgress += 1; - - // Report progress - worker.ReportProgress(currentProgress); - - // See if we're due for a version check - if (DateTime.Now - Settings.Default.LastVersionCheck >= Settings.Default.VersionCheckInterval) + catch (Exception exception) { - // Get the update information - UpdateCheck.CheckForUpdate(Settings.Default.IncludePrerelease).Wait(); - - // Update the last check time - Settings.Default.LastVersionCheck = DateTime.Now; + HandleException(exception); } - - // Increment progress - currentProgress += 1; - - // Report progress - worker.ReportProgress(currentProgress); - - // Sleep for a little bit so the user can see the update - Thread.Sleep(Settings.Default.ProgressSleepInterval * 3); } } \ No newline at end of file diff --git a/Application/MainWindow/MainWindow.xaml.cs b/Application/MainWindow/MainWindow.xaml.cs index 27552e8..0333a6e 100644 --- a/Application/MainWindow/MainWindow.xaml.cs +++ b/Application/MainWindow/MainWindow.xaml.cs @@ -1,5 +1,6 @@ using ChrisKaczor.ApplicationUpdate; using ChrisKaczor.Wpf.Application; +using FeedCenter.Feeds; using FeedCenter.Properties; using Serilog; using System; @@ -29,7 +30,6 @@ public partial class MainWindow : IDisposable public void Dispose() { _mainTimer?.Dispose(); - _feedReadWorker?.Dispose(); GC.SuppressFinalize(this); } @@ -66,12 +66,6 @@ public partial class MainWindow : IDisposable : Brushes.Black; HeaderLabel.Foreground = LinkTextList.Foreground; - // Create the background worker that does the actual reading - _feedReadWorker = new BackgroundWorker { WorkerReportsProgress = true, WorkerSupportsCancellation = true }; - _feedReadWorker.DoWork += HandleFeedReadWorkerStart; - _feedReadWorker.ProgressChanged += HandleFeedReadWorkerProgressChanged; - _feedReadWorker.RunWorkerCompleted += HandleFeedReadWorkerCompleted; - // Set up the database _database = new FeedCenterEntities(); @@ -88,13 +82,16 @@ public partial class MainWindow : IDisposable // Initialize the feed display InitializeDisplay(); - // Check for update - if (Settings.Default.CheckVersionAtStartup) - await UpdateCheck.CheckForUpdate(Settings.Default.IncludePrerelease); + if (UpdateCheck.LocalVersion.Major > 0) + { + // Check for update + if (Settings.Default.CheckVersionAtStartup) + await UpdateCheck.CheckForUpdate(Settings.Default.IncludePrerelease); - // Show the link if updates are available - if (UpdateCheck.UpdateAvailable) - NewVersionLink.Visibility = Visibility.Visible; + // Show the link if updates are available + if (UpdateCheck.UpdateAvailable) + NewVersionLink.Visibility = Visibility.Visible; + } Log.Logger.Information("MainForm creation finished"); } diff --git a/Application/MainWindow/Timer.cs b/Application/MainWindow/Timer.cs index 925752c..ddd3381 100644 --- a/Application/MainWindow/Timer.cs +++ b/Application/MainWindow/Timer.cs @@ -38,29 +38,36 @@ public partial class MainWindow _mainTimer?.Stop(); } - private void HandleMainTimerElapsed(object sender, EventArgs e) + private async void HandleMainTimerElapsed(object sender, EventArgs e) { - _dispatcher.Invoke(() => + try { - // If the background worker is busy then don't do anything - if (_feedReadWorker.IsBusy) - return; + await _dispatcher.Invoke(async () => + { + // If the background worker is busy then don't do anything + if (_reading) + return; - // Stop the timer for now - StopTimer(); + // Stop the timer for now + StopTimer(); - // Move to the next feed if the scroll interval has expired and the mouse isn't hovering - if (LinkTextList.IsMouseOver) - _lastFeedDisplay = DateTime.Now; - else if (DateTime.Now - _lastFeedDisplay >= Settings.Default.FeedScrollInterval) - NextFeed(); + // Move to the next feed if the scroll interval has expired and the mouse isn't hovering + if (LinkTextList.IsMouseOver) + _lastFeedDisplay = DateTime.Now; + else if (DateTime.Now - _lastFeedDisplay >= Settings.Default.FeedScrollInterval) + NextFeed(); - // Check to see if we should try to read the feeds - if (DateTime.Now - _lastFeedRead >= Settings.Default.FeedCheckInterval) - ReadFeeds(); + // Check to see if we should try to read the feeds + if (DateTime.Now - _lastFeedRead >= Settings.Default.FeedCheckInterval) + await ReadFeeds(); - // Get the timer going again - StartTimer(); - }); + // Get the timer going again + StartTimer(); + }); + } + catch (Exception exception) + { + HandleException(exception); + } } } \ No newline at end of file diff --git a/Application/MainWindow/Toolbar.cs b/Application/MainWindow/Toolbar.cs index d82654f..4f1048e 100644 --- a/Application/MainWindow/Toolbar.cs +++ b/Application/MainWindow/Toolbar.cs @@ -9,6 +9,7 @@ using System.Threading.Tasks; using System.Web.UI; using System.Windows; using System.Windows.Controls; +using FeedCenter.Feeds; using Serilog; using Serilog.Events; @@ -75,7 +76,7 @@ public partial class MainWindow UpdateErrorLink(); } - private static void HandleException(Exception exception) + internal static void HandleException(Exception exception) { Log.Logger.Write(LogEventLevel.Debug, exception, ""); } @@ -109,19 +110,33 @@ public partial class MainWindow UpdateErrorLink(); } - private void HandleRefreshMenuItemClick(object sender, RoutedEventArgs e) + private async void HandleRefreshMenuItemClick(object sender, RoutedEventArgs e) { - var menuItem = (MenuItem) e.Source; + try + { + var menuItem = (MenuItem) e.Source; - if (Equals(menuItem, MenuRefresh)) - ReadCurrentFeed(true); - else if (Equals(menuItem, MenuRefreshAll)) - ReadFeeds(true); + if (Equals(menuItem, MenuRefresh)) + ReadCurrentFeed(true); + else if (Equals(menuItem, MenuRefreshAll)) + await ReadFeeds(true); + } + catch (Exception exception) + { + HandleException(exception); + } } - private void HandleRefreshToolbarButtonClick(object sender, RoutedEventArgs e) + private async void HandleRefreshToolbarButtonClick(object sender, RoutedEventArgs e) { - ReadFeeds(true); + try + { + await ReadFeeds(true); + } + catch (Exception exception) + { + HandleException(exception); + } } private async void HandleOpenAllMenuItemClick(object sender, RoutedEventArgs e) diff --git a/Application/MainWindow/WindowHandler.cs b/Application/MainWindow/WindowHandler.cs index 1f3e961..1337c12 100644 --- a/Application/MainWindow/WindowHandler.cs +++ b/Application/MainWindow/WindowHandler.cs @@ -76,13 +76,6 @@ public partial class MainWindow { base.OnClosing(e); - // Ditch the worker - if (_feedReadWorker != null) - { - _feedReadWorker.CancelAsync(); - _feedReadWorker.Dispose(); - } - // Get rid of the timer TerminateTimer(); diff --git a/Application/Options/AccountTypeItem.cs b/Application/Options/AccountTypeItem.cs index e07e5ac..c94f867 100644 --- a/Application/Options/AccountTypeItem.cs +++ b/Application/Options/AccountTypeItem.cs @@ -1,24 +1,29 @@ using System.Collections.Generic; +using FeedCenter.Feeds; -namespace FeedCenter.Options +namespace FeedCenter.Options; + +public class AccountTypeItem { - public class AccountTypeItem - { - public AccountType AccountType { get; set; } - public string Name { get; set; } + public AccountType AccountType { get; set; } + public string Name { get; set; } - public static List AccountTypes => - [ - new() - { - Name = Properties.Resources.AccountTypeFever, - AccountType = AccountType.Fever - }, - //new() - //{ - // Name = Properties.Resources.AccountTypeGoogleReader, - // AccountType = AccountType.GoogleReader - //} - ]; - } + public static List AccountTypes => + [ + new() + { + Name = Properties.Resources.AccountTypeFever, + AccountType = AccountType.Fever + }, + //new() + //{ + // Name = Properties.Resources.AccountTypeGoogleReader, + // AccountType = AccountType.GoogleReader + //} + new() + { + Name = Properties.Resources.AccountTypeMiniflux, + AccountType = AccountType.Miniflux + } + ]; } \ No newline at end of file diff --git a/Application/Options/AccountTypeToNameConverter.cs b/Application/Options/AccountTypeToNameConverter.cs index ce2e5f5..9c916c9 100644 --- a/Application/Options/AccountTypeToNameConverter.cs +++ b/Application/Options/AccountTypeToNameConverter.cs @@ -2,6 +2,7 @@ using System; using System.Globalization; using System.Linq; using System.Windows.Data; +using FeedCenter.Feeds; namespace FeedCenter.Options; diff --git a/Application/Options/AccountWindow.xaml b/Application/Options/AccountWindow.xaml index 9631acb..b712284 100644 --- a/Application/Options/AccountWindow.xaml +++ b/Application/Options/AccountWindow.xaml @@ -4,10 +4,11 @@ xmlns:properties="clr-namespace:FeedCenter.Properties" xmlns:feedCenter="clr-namespace:FeedCenter" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" - d:DataContext="{d:DesignInstance Type=feedCenter:Account}" + d:DataContext="{d:DesignInstance Type=feeds:Account}" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mah="http://metro.mahapps.com/winfx/xaml/controls" xmlns:options="clr-namespace:FeedCenter.Options" + xmlns:feeds="clr-namespace:FeedCenter.Feeds" mc:Ignorable="d" Title="AccountWindow" Height="350" diff --git a/Application/Options/AccountWindow.xaml.cs b/Application/Options/AccountWindow.xaml.cs index 2b9acd1..e332a53 100644 --- a/Application/Options/AccountWindow.xaml.cs +++ b/Application/Options/AccountWindow.xaml.cs @@ -1,6 +1,7 @@ using ChrisKaczor.Wpf.Validation; +using FeedCenter.Feeds; +using System; using System.Linq; -using System.Threading.Tasks; using System.Windows; using System.Windows.Threading; @@ -33,46 +34,46 @@ public partial class AccountWindow return ShowDialog(); } - private void HandleOkayButtonClick(object sender, RoutedEventArgs e) + private async void HandleOkayButtonClick(object sender, RoutedEventArgs e) { - var transaction = _entities.BeginTransaction(); - - if (!this.IsValid(OptionsTabControl)) + try { - transaction.Rollback(); - return; - } + var transaction = _entities.BeginTransaction(); - if (_isNew) - { - _entities.Accounts.Add(_account); - } - - transaction.Commit(); - - var accountId = _account.Id; - - AccountReadProgressBar.Value = 0; - AccountReadProgressBar.Maximum = _account.GetProgressSteps(_entities) + 1; - - AccountReadProgress.Visibility = Visibility.Visible; - ButtonPanel.Visibility = Visibility.Collapsed; - - var dispatcher = Dispatcher.CurrentDispatcher; - - Task.Run(() => - { - var entities = new FeedCenterEntities(); - var account = entities.Accounts.First(a => a.Id == accountId); - var accountReadInput = new AccountReadInput(entities, null, true, () => dispatcher.Invoke(() => AccountReadProgressBar.Value++)); - account.Read(accountReadInput); - - dispatcher.Invoke(() => + if (!this.IsValid(OptionsTabControl)) { - DialogResult = true; + transaction.Rollback(); + return; + } - Close(); - }); - }); + if (_isNew) + { + _entities.Accounts.Add(_account); + } + + await transaction.CommitAsync(); + + var accountId = _account.Id; + + var accountReadInput = new AccountReadInput(_entities, null, true, () => AccountReadProgressBar.Value++); + + AccountReadProgressBar.Value = 0; + AccountReadProgressBar.Maximum = await _account.GetProgressSteps(_account, accountReadInput); + + AccountReadProgress.Visibility = Visibility.Visible; + ButtonPanel.Visibility = Visibility.Collapsed; + + //var entities = new FeedCenterEntities(); + var account = _entities.Accounts.First(a => a.Id == accountId); + await account.Read(accountReadInput); + + DialogResult = true; + + Close(); + } + catch (Exception exception) + { + MainWindow.HandleException(exception); + } } } \ No newline at end of file diff --git a/Application/Options/AccountsOptionsPanel.xaml b/Application/Options/AccountsOptionsPanel.xaml index ec2655a..ba0e369 100644 --- a/Application/Options/AccountsOptionsPanel.xaml +++ b/Application/Options/AccountsOptionsPanel.xaml @@ -7,6 +7,7 @@ xmlns:properties="clr-namespace:FeedCenter.Properties" xmlns:controls="clr-namespace:ChrisKaczor.Wpf.Controls;assembly=ChrisKaczor.Wpf.Controls.Link" xmlns:feedCenter="clr-namespace:FeedCenter" + xmlns:feeds="clr-namespace:FeedCenter.Feeds" mc:Ignorable="d" d:DesignHeight="311" d:DesignWidth="425"> @@ -46,7 +47,7 @@ BorderBrush="{DynamicResource {x:Static SystemColors.ActiveBorderBrushKey}}" AllowDrop="True" Background="{x:Null}" - d:DataContext="{d:DesignInstance feedCenter:Account }"> + d:DataContext="{d:DesignInstance feeds:Account }"> @@ -99,9 +100,9 @@ mah:TextBoxHelper.Watermark="{x:Static my:Resources.openLabel}" IsEnabled="{Binding ElementName=OpenCheckBox, Path=IsChecked}"> + Tag="{x:Static feeds:MultipleOpenAction.IndividualPages}" /> + Tag="{x:Static feeds:MultipleOpenAction.SinglePage}" /> - { - } -} \ No newline at end of file +namespace FeedCenter.Options; + +public class CheckedFeedListItem : CheckedListItem; \ No newline at end of file diff --git a/Application/Options/FeedWindow.xaml b/Application/Options/FeedWindow.xaml index ed92259..43ab679 100644 --- a/Application/Options/FeedWindow.xaml +++ b/Application/Options/FeedWindow.xaml @@ -4,10 +4,11 @@ xmlns:properties="clr-namespace:FeedCenter.Properties" xmlns:feedCenter="clr-namespace:FeedCenter" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" - d:DataContext="{d:DesignInstance Type=feedCenter:Feed}" + d:DataContext="{d:DesignInstance Type=feeds:Feed}" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mah="http://metro.mahapps.com/winfx/xaml/controls" xmlns:options="clr-namespace:FeedCenter.Options" + xmlns:feeds="clr-namespace:FeedCenter.Feeds" mc:Ignorable="d" Title="FeedWindow" Height="350" @@ -90,9 +91,9 @@ mah:TextBoxHelper.UseFloatingWatermark="True" mah:TextBoxHelper.Watermark="{x:Static properties:Resources.openLabel}"> + Tag="{x:Static feeds:MultipleOpenAction.SinglePage}" /> + Tag="{x:Static feeds:MultipleOpenAction.IndividualPages}" /> @@ -45,7 +46,7 @@ BorderBrush="{DynamicResource {x:Static SystemColors.ActiveBorderBrushKey}}" AllowDrop="True" Background="{x:Null}" - d:DataContext="{d:DesignInstance feedCenter:Category }"> + d:DataContext="{d:DesignInstance feeds:Category }"> + d:DataContext="{d:DesignInstance feeds:Feed }"> + /// Looks up a localized string similar to Miniflux. + /// + public static string AccountTypeMiniflux { + get { + return ResourceManager.GetString("AccountTypeMiniflux", resourceCulture); + } + } + /// /// Looks up a localized string similar to URL. /// diff --git a/Application/Properties/Resources.resx b/Application/Properties/Resources.resx index 6ec8617..9f54ccc 100644 --- a/Application/Properties/Resources.resx +++ b/Application/Properties/Resources.resx @@ -614,5 +614,8 @@ All feeds currently in category "{0}" will be moved to the default category. Include _prerelease - + + + Miniflux + \ No newline at end of file