8 Commits

Author SHA1 Message Date
9717e2d6af Fix System.Drawing.Common dependency
- Add System.Drawing.Common explicitly and prevent pruning
- Update other packages
2025-11-24 13:38:18 -05:00
7ace91684c Handle notification icon error until dependency issue gets worked out 2025-11-24 09:54:16 -05:00
c186de0394 Set environment variables 2025-11-22 13:45:45 -05:00
41d0b055dd Add .NET 10 to build image 2025-11-22 13:01:56 -05:00
2d32d740a4 Improve Fever performance 2025-11-22 12:07:05 -05:00
5e7fa4a2e0 Update project 2025-11-15 22:37:45 -05:00
66ea567eaa Start cleaning up account types 2025-11-15 15:30:23 -05:00
6bae35a255 Start adding Miniflux support plus other cleanup
- Modernize old async code
- Update to .NET 10
- Adjust namespace
- Bypass update check when debugging
2025-11-13 10:33:56 -05:00
61 changed files with 614 additions and 389 deletions

View File

@@ -1,10 +1,12 @@
using Realms; using FeedCenter.Feeds;
using Realms;
using System; using System;
using System.Collections; using System.Collections;
using System.ComponentModel; using System.ComponentModel;
using System.Linq; using System.Linq;
using System.Threading.Tasks;
namespace FeedCenter; namespace FeedCenter.Accounts;
public class Account : RealmObject, INotifyDataErrorInfo public class Account : RealmObject, INotifyDataErrorInfo
{ {
@@ -24,6 +26,15 @@ public class Account : RealmObject, INotifyDataErrorInfo
_dataErrorDictionary.ErrorsChanged += DataErrorDictionaryErrorsChanged; _dataErrorDictionary.ErrorsChanged += DataErrorDictionaryErrorsChanged;
} }
private IAccountReader _accountReader;
private IAccountReader GetAccountReader()
{
_accountReader ??= AccountReaderFactory.CreateAccountReader(this);
return _accountReader;
}
[PrimaryKey] [PrimaryKey]
public Guid Id { get; set; } = Guid.NewGuid(); public Guid Id { get; set; } = Guid.NewGuid();
@@ -33,21 +44,9 @@ public class Account : RealmObject, INotifyDataErrorInfo
set => TypeRaw = value.ToString(); set => TypeRaw = value.ToString();
} }
public bool SupportsFeedEdit => Type switch public bool SupportsFeedEdit => GetAccountReader().SupportsFeedEdit;
{
AccountType.Fever => false,
AccountType.GoogleReader => false,
AccountType.Local => true,
_ => throw new NotSupportedException()
};
public bool SupportsFeedDelete => Type switch public bool SupportsFeedDelete => GetAccountReader().SupportsFeedDelete;
{
AccountType.Fever => false,
AccountType.GoogleReader => false,
AccountType.Local => true,
_ => throw new NotSupportedException()
};
private string TypeRaw { get; set; } private string TypeRaw { get; set; }
@@ -158,21 +157,14 @@ public class Account : RealmObject, INotifyDataErrorInfo
return new Account { Name = DefaultName, Type = AccountType.Local }; return new Account { Name = DefaultName, Type = AccountType.Local };
} }
public int GetProgressSteps(FeedCenterEntities entities) public async Task<int> GetProgressSteps(AccountReadInput accountReadInput)
{ {
var progressSteps = Type switch var progressSteps = await GetAccountReader().GetProgressSteps(accountReadInput);
{
// 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),
_ => throw new NotSupportedException()
};
return progressSteps; return progressSteps;
} }
public AccountReadResult Read(AccountReadInput accountReadInput) public async Task<AccountReadResult> Read(AccountReadInput accountReadInput)
{ {
// If not enabled then do nothing // If not enabled then do nothing
if (!Enabled) if (!Enabled)
@@ -189,14 +181,7 @@ public class Account : RealmObject, INotifyDataErrorInfo
return AccountReadResult.NotDue; return AccountReadResult.NotDue;
} }
var accountReadResult = Type switch var accountReadResult = await GetAccountReader().Read(accountReadInput);
{
// 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()
};
return accountReadResult; return accountReadResult;
} }

View File

@@ -1,6 +1,6 @@
using System; using System;
namespace FeedCenter; namespace FeedCenter.Accounts;
public class AccountReadInput(FeedCenterEntities entities, Guid? feedId, bool forceRead, Action incrementProgress) public class AccountReadInput(FeedCenterEntities entities, Guid? feedId, bool forceRead, Action incrementProgress)
{ {

View File

@@ -1,4 +1,4 @@
namespace FeedCenter; namespace FeedCenter.Accounts;
public enum AccountReadResult public enum AccountReadResult
{ {

View File

@@ -0,0 +1,16 @@
using System;
namespace FeedCenter.Accounts;
internal static class AccountReaderFactory
{
internal static IAccountReader CreateAccountReader(Account account) =>
account.Type switch
{
AccountType.Miniflux => new MinifluxReader(account),
AccountType.Local => new LocalReader(account),
AccountType.Fever => new FeverReader(account),
AccountType.GoogleReader => new GoogleReaderReader(account),
_ => throw new NotSupportedException($"Account type '{account.Type}' is not supported."),
};
}

View File

@@ -0,0 +1,9 @@
namespace FeedCenter.Accounts;
public enum AccountType
{
Local,
Fever,
GoogleReader,
Miniflux
}

View File

@@ -1,20 +1,30 @@
using System; using ChrisKaczor.FeverClient;
using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Security.Cryptography; using System.Security.Cryptography;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using ChrisKaczor.FeverClient; using FeedCenter.Feeds;
namespace FeedCenter; namespace FeedCenter.Accounts;
internal class FeverReader : IAccountReader using FeverFeedItem = ChrisKaczor.FeverClient.Models.FeedItem;
internal class FeverReader(Account account) : IAccountReader
{ {
public int GetProgressSteps(FeedCenterEntities entities) public async Task<int> 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<AccountReadResult> Read(AccountReadInput accountReadInput)
{ {
var checkTime = DateTimeOffset.UtcNow; var checkTime = DateTimeOffset.UtcNow;
@@ -24,19 +34,21 @@ internal class FeverReader : IAccountReader
accountReadInput.IncrementProgress(); accountReadInput.IncrementProgress();
var feverFeeds = feverClient.GetFeeds().Result.ToList(); var feverFeeds = (await feverClient.GetFeeds()).ToList();
accountReadInput.IncrementProgress(); accountReadInput.IncrementProgress();
var allFeverFeedItems = feverClient.GetAllFeedItems().Result.ToList(); var allFeverFeedItems = await GetAllFeverFeedItems(feverClient);
accountReadInput.IncrementProgress(); accountReadInput.IncrementProgress();
var existingFeedsByRemoteId = accountReadInput.Entities.Feeds.Where(f => f.Account.Id == account.Id) .ToDictionary(f => f.RemoteId);
var transaction = accountReadInput.Entities.BeginTransaction(); var transaction = accountReadInput.Entities.BeginTransaction();
foreach (var feverFeed in feverFeeds) foreach (var feverFeed in feverFeeds)
{ {
var feed = accountReadInput.Entities.Feeds.FirstOrDefault(f => f.RemoteId == feverFeed.Id.ToString() && f.Account.Id == account.Id); var feed = existingFeedsByRemoteId.GetValueOrDefault(feverFeed.Id.ToString(), null);
if (feed == null) if (feed == null)
{ {
@@ -66,15 +78,15 @@ internal class FeverReader : IAccountReader
accountReadInput.IncrementProgress(); accountReadInput.IncrementProgress();
var feverFeedItems = allFeverFeedItems var feverFeedItems = allFeverFeedItems.GetValueOrDefault(feverFeed.Id, []);
.Where(f => f.FeedId == feverFeed.Id)
.OrderByDescending(fi => fi.CreatedOnTime).ToList(); var existingFeedItemsByRemoteId = feed.Items.ToDictionary(fi => fi.RemoteId);
var sequence = 1; var sequence = 1;
foreach (var feverFeedItem in feverFeedItems) foreach (var feverFeedItem in feverFeedItems)
{ {
var feedItem = feed.Items.FirstOrDefault(f => f.RemoteId == feverFeedItem.Id.ToString()); var feedItem = existingFeedItemsByRemoteId.GetValueOrDefault(feverFeedItem.Id.ToString(), null);
if (feedItem == null) if (feedItem == null)
{ {
@@ -120,14 +132,28 @@ internal class FeverReader : IAccountReader
account.LastChecked = checkTime; account.LastChecked = checkTime;
transaction.Commit(); await transaction.CommitAsync();
accountReadInput.IncrementProgress(); accountReadInput.IncrementProgress();
return AccountReadResult.Success; return AccountReadResult.Success;
} }
public static async Task MarkFeedItemRead(Account account, string feedItemId) private static async Task<Dictionary<int, List<FeverFeedItem>>> GetAllFeverFeedItems(FeverClient feverClient)
{
var allFeverFeedItems = new List<FeverFeedItem>();
await foreach (var page in feverClient.GetAllFeedItems())
{
allFeverFeedItems.AddRange(page);
}
var grouped = allFeverFeedItems.OrderByDescending(fi => fi.CreatedOnTime).GroupBy(fi => fi.FeedId);
return grouped.ToDictionary(g => g.Key, g => g.ToList());
}
public async Task MarkFeedItemRead(string feedItemId)
{ {
var apiKey = account.Authenticate ? GetApiKey(account) : string.Empty; var apiKey = account.Authenticate ? GetApiKey(account) : string.Empty;
@@ -136,6 +162,10 @@ internal class FeverReader : IAccountReader
await feverClient.MarkFeedItemAsRead(int.Parse(feedItemId)); await feverClient.MarkFeedItemAsRead(int.Parse(feedItemId));
} }
public bool SupportsFeedDelete => false;
public bool SupportsFeedEdit => false;
private static string GetApiKey(Account account) private static string GetApiKey(Account account)
{ {
var input = $"{account.Username}:{account.Password}"; var input = $"{account.Username}:{account.Password}";

View File

@@ -1,19 +1,16 @@
using System; using System;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace FeedCenter; namespace FeedCenter.Accounts;
internal class GoogleReaderReader : IAccountReader internal class GoogleReaderReader(Account account) : IAccountReader
{ {
public int GetProgressSteps(FeedCenterEntities entities) public Task<int> GetProgressSteps(AccountReadInput accountReadInput)
{ {
return 7; return Task.FromResult(7);
} }
public AccountReadResult Read(Account account, AccountReadInput accountReadInput) public async Task<AccountReadResult> Read(AccountReadInput accountReadInput)
{ {
var checkTime = DateTimeOffset.UtcNow; var checkTime = DateTimeOffset.UtcNow;
@@ -119,22 +116,28 @@ internal class GoogleReaderReader : IAccountReader
account.LastChecked = checkTime; account.LastChecked = checkTime;
transaction.Commit(); await transaction.CommitAsync();
accountReadInput.IncrementProgress(); accountReadInput.IncrementProgress();
return AccountReadResult.Success; 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 apiKey = account.Authenticate ? GetApiKey(account) : string.Empty;
//var feverClient = new FeverClient.FeverClient(account.Url, apiKey); //var feverClient = new FeverClient.FeverClient(account.Url, apiKey);
//await feverClient.MarkFeedItemAsRead(int.Parse(feedItemId)); //await feverClient.MarkFeedItemAsRead(int.Parse(feedItemId));
return Task.CompletedTask;
} }
public bool SupportsFeedDelete => false;
public bool SupportsFeedEdit => false;
//private static string GetApiKey(Account account) //private static string GetApiKey(Account account)
//{ //{
// var input = $"{account.Username}:{account.Password}"; // var input = $"{account.Username}:{account.Password}";

View File

@@ -0,0 +1,12 @@
using System.Threading.Tasks;
namespace FeedCenter.Accounts;
public interface IAccountReader
{
public Task<int> GetProgressSteps(AccountReadInput accountReadInput);
public Task<AccountReadResult> Read(AccountReadInput accountReadInput);
public Task MarkFeedItemRead(string feedItemId);
public bool SupportsFeedDelete { get; }
public bool SupportsFeedEdit { get; }
}

View File

@@ -1,19 +1,21 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks;
using FeedCenter.Feeds;
namespace FeedCenter; namespace FeedCenter.Accounts;
public class LocalReader : IAccountReader public class LocalReader(Account account) : IAccountReader
{ {
public int GetProgressSteps(FeedCenterEntities entities) public Task<int> 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<AccountReadResult> Read(AccountReadInput accountReadInput)
{ {
var checkTime = DateTimeOffset.UtcNow; var checkTime = DateTimeOffset.UtcNow;
@@ -37,6 +39,15 @@ public class LocalReader : IAccountReader
accountReadInput.Entities.SaveChanges(() => account.LastChecked = checkTime); accountReadInput.Entities.SaveChanges(() => account.LastChecked = checkTime);
return AccountReadResult.Success; return Task.FromResult(AccountReadResult.Success);
} }
public Task MarkFeedItemRead(string feedItemId)
{
throw new NotImplementedException();
}
public bool SupportsFeedDelete => true;
public bool SupportsFeedEdit => true;
} }

View File

@@ -0,0 +1,151 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using ChrisKaczor.MinifluxClient;
using FeedCenter.Feeds;
namespace FeedCenter.Accounts;
internal class MinifluxReader(Account account) : IAccountReader
{
public async Task<int> 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<AccountReadResult> 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);
}
public bool SupportsFeedDelete => true;
public bool SupportsFeedEdit => true;
}

View File

@@ -35,8 +35,8 @@ public partial class App
var app = new App(); var app = new App();
app.InitializeComponent(); app.InitializeComponent();
// Create an single instance handle to see if we are already running // Create a single instance handle to see if we are already running
var isolationHandle = SingleInstance.GetSingleInstanceHandleAsync(Name).Result; var isolationHandle = SingleInstance.GetSingleInstanceHandleAsync(Name).GetAwaiter().GetResult();
// If there is another copy then pass it the command line and exit // If there is another copy then pass it the command line and exit
if (isolationHandle == null) if (isolationHandle == null)

View File

@@ -8,6 +8,7 @@ using System.Collections.Generic;
using System.Data.SqlServerCe; using System.Data.SqlServerCe;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using FeedCenter.Feeds;
namespace FeedCenter.Data; namespace FeedCenter.Data;

View File

@@ -3,6 +3,8 @@ using FeedCenter.Options;
using Realms; using Realms;
using System; using System;
using System.Linq; using System.Linq;
using FeedCenter.Accounts;
using FeedCenter.Feeds;
namespace FeedCenter; namespace FeedCenter;

View File

@@ -1,11 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net9.0-windows8.0</TargetFramework> <TargetFramework>net10.0-windows8.0</TargetFramework>
<OutputType>WinExe</OutputType> <OutputType>WinExe</OutputType>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo> <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<UseWindowsForms>false</UseWindowsForms> <UseWindowsForms>false</UseWindowsForms>
<UseWPF>true</UseWPF> <UseWPF>true</UseWPF>
<ImportWindowsDesktopTargets>true</ImportWindowsDesktopTargets> <ImportWindowsDesktopTargets>true</ImportWindowsDesktopTargets>
<RestoreEnablePackagePruning>false</RestoreEnablePackagePruning>
</PropertyGroup> </PropertyGroup>
<PropertyGroup> <PropertyGroup>
<EnableDefaultApplicationDefinition>false</EnableDefaultApplicationDefinition> <EnableDefaultApplicationDefinition>false</EnableDefaultApplicationDefinition>
@@ -25,9 +26,10 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="ChrisKaczor.ApplicationUpdate" Version="1.0.7" /> <PackageReference Include="ChrisKaczor.ApplicationUpdate" Version="1.0.7" />
<PackageReference Include="ChrisKaczor.FeverClient" Version="1.0.1" /> <PackageReference Include="ChrisKaczor.FeverClient" Version="1.0.3" />
<PackageReference Include="ChrisKaczor.GenericSettingsProvider" Version="1.0.4" /> <PackageReference Include="ChrisKaczor.GenericSettingsProvider" Version="1.0.4" />
<PackageReference Include="ChrisKaczor.InstalledBrowsers" Version="1.0.4" /> <PackageReference Include="ChrisKaczor.InstalledBrowsers" Version="1.0.4" />
<PackageReference Include="ChrisKaczor.MinifluxClient" Version="1.0.3" />
<PackageReference Include="ChrisKaczor.Wpf.Application.SingleInstance" Version="1.0.5" /> <PackageReference Include="ChrisKaczor.Wpf.Application.SingleInstance" Version="1.0.5" />
<PackageReference Include="ChrisKaczor.Wpf.Application.StartWithWindows" Version="1.0.5" /> <PackageReference Include="ChrisKaczor.Wpf.Application.StartWithWindows" Version="1.0.5" />
<PackageReference Include="ChrisKaczor.Wpf.Controls.HtmlTextBlock" Version="1.0.6" /> <PackageReference Include="ChrisKaczor.Wpf.Controls.HtmlTextBlock" Version="1.0.6" />
@@ -38,23 +40,24 @@
<PackageReference Include="ChrisKaczor.Wpf.Windows.SnappingWindow" Version="1.0.4" /> <PackageReference Include="ChrisKaczor.Wpf.Windows.SnappingWindow" Version="1.0.4" />
<PackageReference Include="Dapper" Version="2.1.66" /> <PackageReference Include="Dapper" Version="2.1.66" />
<PackageReference Include="DebounceThrottle" Version="3.0.1" /> <PackageReference Include="DebounceThrottle" Version="3.0.1" />
<PackageReference Include="H.NotifyIcon.Wpf" Version="2.3.0" /> <PackageReference Include="H.NotifyIcon.Wpf" Version="2.3.2" />
<PackageReference Include="HtmlAgilityPack" Version="1.12.3" /> <PackageReference Include="HtmlAgilityPack" Version="1.12.4" />
<PackageReference Include="HtmlTextWriter" Version="3.0.2" /> <PackageReference Include="HtmlTextWriter" Version="3.0.2" />
<PackageReference Include="MahApps.Metro" Version="2.4.11" /> <PackageReference Include="MahApps.Metro" Version="2.4.11" />
<PackageReference Include="Microsoft.SqlServer.Compact" Version="4.0.8876.1" GeneratePathProperty="true"> <PackageReference Include="Microsoft.SqlServer.Compact" Version="4.0.8876.1" GeneratePathProperty="true">
<NoWarn>NU1701</NoWarn> <NoWarn>NU1701</NoWarn>
</PackageReference> </PackageReference>
<PackageReference Include="Microsoft.Windows.Compatibility" Version="9.0.9" /> <PackageReference Include="Microsoft.Windows.Compatibility" Version="10.0.0" />
<PackageReference Include="NameBasedGrid" Version="1.0.0"> <PackageReference Include="NameBasedGrid" Version="1.0.0">
<NoWarn>NU1701</NoWarn> <NoWarn>NU1701</NoWarn>
</PackageReference> </PackageReference>
<PackageReference Include="Realm" Version="20.1.0" /> <PackageReference Include="Realm" Version="20.1.0" />
<PackageReference Include="Serilog" Version="4.3.0" /> <PackageReference Include="Serilog" Version="4.3.0" />
<PackageReference Include="Serilog.Enrichers.Thread" Version="4.0.0" /> <PackageReference Include="Serilog.Enrichers.Thread" Version="4.0.0" />
<PackageReference Include="Serilog.Sinks.Console" Version="6.0.0" /> <PackageReference Include="Serilog.Sinks.Console" Version="6.1.1" />
<PackageReference Include="Serilog.Sinks.File" Version="7.0.0" /> <PackageReference Include="Serilog.Sinks.File" Version="7.0.0" />
<PackageReference Include="System.ComponentModel.Annotations" Version="5.0.0" /> <PackageReference Include="System.ComponentModel.Annotations" Version="5.0.0" />
<PackageReference Include="System.Drawing.Common" Version="10.0.0" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Resource Include="Resources\Application.ico" /> <Resource Include="Resources\Application.ico" />

View File

@@ -1,3 +1,4 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation"> <wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=Feeds/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=feeds/@EntryIndexedValue">False</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=mainwindow/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary> <s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=mainwindow/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>

View File

@@ -4,4 +4,5 @@
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpPlaceEmbeddedOnSameLineMigration/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpPlaceEmbeddedOnSameLineMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpUseContinuousIndentInsideBracesMigration/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpUseContinuousIndentInsideBracesMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateBlankLinesAroundFieldToBlankLinesAroundProperty/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateBlankLinesAroundFieldToBlankLinesAroundProperty/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Kaczor/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary> <s:Boolean x:Key="/Default/UserDictionary/Words/=Kaczor/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Miniflux/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>

View File

@@ -4,6 +4,7 @@
xmlns:linkControl="clr-namespace:ChrisKaczor.Wpf.Controls;assembly=ChrisKaczor.Wpf.Controls.Link" 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: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:feedCenter="clr-namespace:FeedCenter"
xmlns:feeds="clr-namespace:FeedCenter.Feeds"
mc:Ignorable="d" mc:Ignorable="d"
x:Class="FeedCenter.FeedErrorWindow" x:Class="FeedCenter.FeedErrorWindow"
Title="{x:Static my:Resources.FeedErrorWindow}" Title="{x:Static my:Resources.FeedErrorWindow}"
@@ -46,7 +47,7 @@
HeadersVisibility="Column" HeadersVisibility="Column"
Background="{x:Null}" Background="{x:Null}"
CanUserSortColumns="True" CanUserSortColumns="True"
d:DataContext="{d:DesignInstance Type=feedCenter:Feed}" SelectionChanged="FeedDataGrid_SelectionChanged"> d:DataContext="{d:DesignInstance Type=feeds:Feed}" SelectionChanged="FeedDataGrid_SelectionChanged">
<DataGrid.Columns> <DataGrid.Columns>
<DataGridTextColumn Header="{x:Static my:Resources.FeedNameColumnHeader}" <DataGridTextColumn Header="{x:Static my:Resources.FeedNameColumnHeader}"
Binding="{Binding Name}" Binding="{Binding Name}"

View File

@@ -5,6 +5,7 @@ using System.Windows;
using System.Windows.Data; using System.Windows.Data;
using System.Windows.Input; using System.Windows.Input;
using ChrisKaczor.InstalledBrowsers; using ChrisKaczor.InstalledBrowsers;
using FeedCenter.Feeds;
using FeedCenter.Options; using FeedCenter.Options;
using FeedCenter.Properties; using FeedCenter.Properties;

View File

@@ -1,6 +1,7 @@
using Serilog; using Serilog;
using System; using System;
using System.Xml; using System.Xml;
using FeedCenter.Feeds;
namespace FeedCenter.FeedParsers; namespace FeedCenter.FeedParsers;

View File

@@ -2,6 +2,7 @@
using System; using System;
using System.Linq; using System.Linq;
using System.Xml; using System.Xml;
using FeedCenter.Feeds;
namespace FeedCenter.FeedParsers; namespace FeedCenter.FeedParsers;

View File

@@ -1,6 +1,7 @@
using FeedCenter.Xml; using FeedCenter.Xml;
using Serilog; using Serilog;
using System.Xml; using System.Xml;
using FeedCenter.Feeds;
namespace FeedCenter.FeedParsers; namespace FeedCenter.FeedParsers;

View File

@@ -2,6 +2,7 @@
using Serilog; using Serilog;
using System; using System;
using System.Xml; using System.Xml;
using FeedCenter.Feeds;
namespace FeedCenter.FeedParsers; namespace FeedCenter.FeedParsers;

View File

@@ -1,8 +0,0 @@
namespace FeedCenter;
public enum AccountType
{
Local,
Fever,
GoogleReader
}

View File

@@ -1,11 +1,12 @@
using JetBrains.Annotations; using System;
using Realms;
using System;
using System.Collections; using System.Collections;
using System.ComponentModel; using System.ComponentModel;
using System.Linq; using System.Linq;
using FeedCenter.Accounts;
using JetBrains.Annotations;
using Realms;
namespace FeedCenter; namespace FeedCenter.Feeds;
public class Category : RealmObject, INotifyDataErrorInfo public class Category : RealmObject, INotifyDataErrorInfo
{ {

View File

@@ -3,7 +3,7 @@ using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel; using System.ComponentModel;
namespace FeedCenter; namespace FeedCenter.Feeds;
internal class DataErrorDictionary : Dictionary<string, List<string>> internal class DataErrorDictionary : Dictionary<string, List<string>>
{ {

View File

@@ -12,6 +12,7 @@ using System.Text;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Threading.Tasks; using System.Threading.Tasks;
using ChrisKaczor.ApplicationUpdate; using ChrisKaczor.ApplicationUpdate;
using FeedCenter.Accounts;
using FeedCenter.FeedParsers; using FeedCenter.FeedParsers;
using FeedCenter.Properties; using FeedCenter.Properties;
using FeedCenter.Xml; using FeedCenter.Xml;
@@ -19,7 +20,7 @@ using JetBrains.Annotations;
using Realms; using Realms;
using Serilog; using Serilog;
namespace FeedCenter; namespace FeedCenter.Feeds;
public partial class Feed : RealmObject, INotifyDataErrorInfo public partial class Feed : RealmObject, INotifyDataErrorInfo
{ {

View File

@@ -1,12 +1,12 @@
using FeedCenter.Options; using System;
using Realms;
using System;
using System.Diagnostics;
using System.Linq; using System.Linq;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Threading.Tasks; using System.Threading.Tasks;
using FeedCenter.Accounts;
using FeedCenter.Options;
using Realms;
namespace FeedCenter; namespace FeedCenter.Feeds;
public partial class FeedItem : RealmObject public partial class FeedItem : RealmObject
{ {
@@ -86,19 +86,7 @@ public partial class FeedItem : RealmObject
if (feed == null || feed.Account.Type == AccountType.Local) if (feed == null || feed.Account.Type == AccountType.Local)
return; return;
switch (feed.Account.Type) await AccountReaderFactory.CreateAccountReader(feed.Account).MarkFeedItemRead(RemoteId);
{
case AccountType.Fever:
// Delegate to the right reader based on the account type
await FeverReader.MarkFeedItemRead(feed.Account, RemoteId);
break;
case AccountType.Local:
break;
default:
Debug.Assert(false);
break;
}
} }
[GeneratedRegex("\\n")] [GeneratedRegex("\\n")]

View File

@@ -1,4 +1,4 @@
namespace FeedCenter; namespace FeedCenter.Feeds;
public enum FeedReadResult public enum FeedReadResult
{ {

View File

@@ -1,4 +1,4 @@
namespace FeedCenter; namespace FeedCenter.Feeds;
public enum FeedType public enum FeedType
{ {

View File

@@ -1,7 +0,0 @@
namespace FeedCenter;
public interface IAccountReader
{
public int GetProgressSteps(FeedCenterEntities entities);
public AccountReadResult Read(Account account, AccountReadInput accountReadInput);
}

View File

@@ -1,4 +1,4 @@
namespace FeedCenter; namespace FeedCenter.Feeds;
public enum MultipleOpenAction public enum MultipleOpenAction
{ {

View File

@@ -3,6 +3,7 @@ using System;
using System.Linq; using System.Linq;
using System.Windows; using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
using FeedCenter.Feeds;
namespace FeedCenter; namespace FeedCenter;

View File

@@ -3,6 +3,7 @@ using System;
using System.Linq; using System.Linq;
using System.Net; using System.Net;
using System.Threading; using System.Threading;
using FeedCenter.Feeds;
namespace FeedCenter; namespace FeedCenter;

View File

@@ -2,7 +2,6 @@
using FeedCenter.Properties; using FeedCenter.Properties;
using System; using System;
using System.Linq; using System.Linq;
using System.Threading.Tasks;
using System.Windows; using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
using System.Windows.Input; using System.Windows.Input;
@@ -38,7 +37,7 @@ public partial class MainWindow
return; return;
// Get the feed item // Get the feed item
var feedItem = (FeedItem) ((ListBoxItem) sender).DataContext; var feedItem = (Feeds.FeedItem) ((ListBoxItem) sender).DataContext;
// Remove the item from the list // Remove the item from the list
LinkTextList.Items.Remove(feedItem); LinkTextList.Items.Remove(feedItem);
@@ -50,7 +49,7 @@ public partial class MainWindow
private async void HandleItemMouseDoubleClick(object sender, MouseButtonEventArgs e) private async void HandleItemMouseDoubleClick(object sender, MouseButtonEventArgs e)
{ {
// Get the feed item // Get the feed item
var feedItem = (FeedItem) ((ListBoxItem) sender).DataContext; var feedItem = (Feeds.FeedItem) ((ListBoxItem) sender).DataContext;
// Try to open the item link // Try to open the item link
if (!InstalledBrowser.OpenLink(Settings.Default.Browser, feedItem.Link)) if (!InstalledBrowser.OpenLink(Settings.Default.Browser, feedItem.Link))
@@ -104,7 +103,7 @@ public partial class MainWindow
var menuItem = (MenuItem) sender; var menuItem = (MenuItem) sender;
// Get the feed from the menu item tab // 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 // Loop over all feeds and look for the index of the new one
var feedIndex = 0; var feedIndex = 0;

View File

@@ -1,38 +1,20 @@
using ChrisKaczor.ApplicationUpdate; using ChrisKaczor.ApplicationUpdate;
using FeedCenter.Feeds;
using FeedCenter.Properties; using FeedCenter.Properties;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel; using System.Diagnostics;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks;
using System.Windows; using System.Windows;
using FeedCenter.Accounts;
namespace FeedCenter; namespace FeedCenter;
public partial class MainWindow public partial class MainWindow
{ {
private BackgroundWorker _feedReadWorker; private bool _reading;
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 void SetProgressMode(bool showProgress, int maximum) private void SetProgressMode(bool showProgress, int maximum)
{ {
@@ -49,55 +31,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 try
if (_feedReadWorker.IsBusy) {
return; // Don't read if we're already working
if (_reading)
return;
// Don't read if there is nothing to read _reading = true;
if (!_database.Feeds.Any())
return;
// Switch to progress mode // Don't read if there is nothing to read
SetProgressMode(true, 1); if (!_database.Feeds.Any())
return;
// Create the input class var accountReadInput = new AccountReadInput(_database, _currentFeed.Id, forceRead, () => { });
var workerInput = new FeedReadWorkerInput(forceRead, _currentFeed.Id);
// Start the worker // Switch to progress mode
_feedReadWorker.RunWorkerAsync(workerInput); SetProgressMode(true, await _currentFeed.Account.GetProgressSteps(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 // Don't read if we're already working
if (_feedReadWorker.IsBusy) if (_reading)
return; return;
_reading = true;
// Don't read if there is nothing to read // Don't read if there is nothing to read
if (!_database.Accounts.Any()) if (!_database.Accounts.Any())
return; 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(accountReadInput);
}
// Switch to progress mode // Switch to progress mode
SetProgressMode(true, progressSteps); SetProgressMode(true, progressSteps);
// Create the input class // Start reading
var workerInput = new FeedReadWorkerInput(forceRead); await HandleFeedReadWorkerStart(forceRead, null);
// Start the worker
_feedReadWorker.RunWorkerAsync(workerInput);
} }
private void HandleFeedReadWorkerProgressChanged(object sender, ProgressChangedEventArgs e) private void IncrementProgress()
{ {
Debug.Assert(FeedReadProgress.Value + 1 <= FeedReadProgress.Maximum);
// Set progress // Set progress
FeedReadProgress.Value = e.ProgressPercentage; FeedReadProgress.Value++;
} }
private void HandleFeedReadWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) private void CompleteRead()
{ {
// Refresh the database to current settings // Refresh the database to current settings
ResetDatabase(); ResetDatabase();
@@ -119,6 +118,8 @@ public partial class MainWindow
NewVersionLink.Visibility = Visibility.Visible; NewVersionLink.Visibility = Visibility.Visible;
UpdateErrorLink(); UpdateErrorLink();
_reading = false;
} }
private void UpdateErrorLink() private void UpdateErrorLink()
@@ -134,98 +135,61 @@ public partial class MainWindow
: string.Format(Properties.Resources.FeedErrorsLink, feedErrorCount); : 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 try
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<Account>();
// If we have a single feed then get the account for that feed
if (workerInput.FeedId != null)
{ {
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) var accountsToRead = new List<Account>();
accountsToRead.Add(feed.Account);
}
else
{
// Otherwise get all accounts
accountsToRead.AddRange(database.Accounts);
}
var incrementProgress = () => // If we have a single feed then get the account for that feed
{ if (feedId != null)
// Increment progress {
currentProgress += 1; 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 // Report progress
worker.ReportProgress(currentProgress); IncrementProgress();
};
// Loop over each account and read it // See if we're due for a version check
foreach (var account in accountsToRead) if (UpdateCheck.LocalVersion.Major > 0 && DateTime.Now - Settings.Default.LastVersionCheck >= Settings.Default.VersionCheckInterval)
{ {
var accountReadInput = new AccountReadInput(database, workerInput.FeedId, workerInput.ForceRead, incrementProgress); // 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();
} }
catch (Exception exception)
//// Create the list of feeds to read
//var feedsToRead = new List<Feed>();
//// 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)
{ {
// Get the update information HandleException(exception);
UpdateCheck.CheckForUpdate(Settings.Default.IncludePrerelease).Wait();
// Update the last check time
Settings.Default.LastVersionCheck = DateTime.Now;
} }
// 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);
} }
} }

View File

@@ -1,5 +1,6 @@
using ChrisKaczor.ApplicationUpdate; using ChrisKaczor.ApplicationUpdate;
using ChrisKaczor.Wpf.Application; using ChrisKaczor.Wpf.Application;
using FeedCenter.Feeds;
using FeedCenter.Properties; using FeedCenter.Properties;
using Serilog; using Serilog;
using System; using System;
@@ -29,7 +30,6 @@ public partial class MainWindow : IDisposable
public void Dispose() public void Dispose()
{ {
_mainTimer?.Dispose(); _mainTimer?.Dispose();
_feedReadWorker?.Dispose();
GC.SuppressFinalize(this); GC.SuppressFinalize(this);
} }
@@ -54,9 +54,6 @@ public partial class MainWindow : IDisposable
// Set up the update handler // Set up the update handler
InitializeUpdate(); InitializeUpdate();
// Show the notification icon
NotificationIcon.Initialize(this);
// Load window settings // Load window settings
LoadWindowSettings(); LoadWindowSettings();
@@ -66,12 +63,6 @@ public partial class MainWindow : IDisposable
: Brushes.Black; : Brushes.Black;
HeaderLabel.Foreground = LinkTextList.Foreground; 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 // Set up the database
_database = new FeedCenterEntities(); _database = new FeedCenterEntities();
@@ -88,13 +79,26 @@ public partial class MainWindow : IDisposable
// Initialize the feed display // Initialize the feed display
InitializeDisplay(); InitializeDisplay();
// Check for update if (UpdateCheck.LocalVersion.Major > 0)
if (Settings.Default.CheckVersionAtStartup) {
await UpdateCheck.CheckForUpdate(Settings.Default.IncludePrerelease); // Check for update
if (Settings.Default.CheckVersionAtStartup)
await UpdateCheck.CheckForUpdate(Settings.Default.IncludePrerelease);
// Show the link if updates are available // Show the link if updates are available
if (UpdateCheck.UpdateAvailable) if (UpdateCheck.UpdateAvailable)
NewVersionLink.Visibility = Visibility.Visible; NewVersionLink.Visibility = Visibility.Visible;
}
try
{
// Show the notification icon
NotificationIcon.Initialize(this);
}
catch (Exception e)
{
Log.Logger.Error(e, "");
}
Log.Logger.Information("MainForm creation finished"); Log.Logger.Information("MainForm creation finished");
} }

View File

@@ -38,29 +38,36 @@ public partial class MainWindow
_mainTimer?.Stop(); _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 await _dispatcher.Invoke(async () =>
if (_feedReadWorker.IsBusy) {
return; // If the background worker is busy then don't do anything
if (_reading)
return;
// Stop the timer for now // Stop the timer for now
StopTimer(); StopTimer();
// Move to the next feed if the scroll interval has expired and the mouse isn't hovering // Move to the next feed if the scroll interval has expired and the mouse isn't hovering
if (LinkTextList.IsMouseOver) if (LinkTextList.IsMouseOver)
_lastFeedDisplay = DateTime.Now; _lastFeedDisplay = DateTime.Now;
else if (DateTime.Now - _lastFeedDisplay >= Settings.Default.FeedScrollInterval) else if (DateTime.Now - _lastFeedDisplay >= Settings.Default.FeedScrollInterval)
NextFeed(); NextFeed();
// Check to see if we should try to read the feeds // Check to see if we should try to read the feeds
if (DateTime.Now - _lastFeedRead >= Settings.Default.FeedCheckInterval) if (DateTime.Now - _lastFeedRead >= Settings.Default.FeedCheckInterval)
ReadFeeds(); await ReadFeeds();
// Get the timer going again // Get the timer going again
StartTimer(); StartTimer();
}); });
}
catch (Exception exception)
{
HandleException(exception);
}
} }
} }

View File

@@ -9,6 +9,7 @@ using System.Threading.Tasks;
using System.Web.UI; using System.Web.UI;
using System.Windows; using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
using FeedCenter.Feeds;
using Serilog; using Serilog;
using Serilog.Events; using Serilog.Events;
@@ -75,7 +76,7 @@ public partial class MainWindow
UpdateErrorLink(); UpdateErrorLink();
} }
private static void HandleException(Exception exception) internal static void HandleException(Exception exception)
{ {
Log.Logger.Write(LogEventLevel.Debug, exception, ""); Log.Logger.Write(LogEventLevel.Debug, exception, "");
} }
@@ -109,19 +110,33 @@ public partial class MainWindow
UpdateErrorLink(); 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)) if (Equals(menuItem, MenuRefresh))
ReadCurrentFeed(true); ReadCurrentFeed(true);
else if (Equals(menuItem, MenuRefreshAll)) else if (Equals(menuItem, MenuRefreshAll))
ReadFeeds(true); 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) private async void HandleOpenAllMenuItemClick(object sender, RoutedEventArgs e)

View File

@@ -76,13 +76,6 @@ public partial class MainWindow
{ {
base.OnClosing(e); base.OnClosing(e);
// Ditch the worker
if (_feedReadWorker != null)
{
_feedReadWorker.CancelAsync();
_feedReadWorker.Dispose();
}
// Get rid of the timer // Get rid of the timer
TerminateTimer(); TerminateTimer();

View File

@@ -19,7 +19,7 @@ internal static class NotificationIcon
_notificationIcon = new TaskbarIcon { Icon = Resources.Application }; _notificationIcon = new TaskbarIcon { Icon = Resources.Application };
_notificationIcon.TrayMouseDoubleClick += HandleNotificationIconDoubleClick; _notificationIcon.TrayMouseDoubleClick += HandleNotificationIconDoubleClick;
// Setup the menu // Set up the menu
var contextMenu = new ContextMenu(); var contextMenu = new ContextMenu();
contextMenu.Opened += HandleContextMenuOpened; contextMenu.Opened += HandleContextMenuOpened;
@@ -76,7 +76,7 @@ internal static class NotificationIcon
public static void Dispose() public static void Dispose()
{ {
// Get rid of the icon // Get rid of the icon
_notificationIcon.Dispose(); _notificationIcon?.Dispose();
_notificationIcon = null; _notificationIcon = null;
_mainWindow = null; _mainWindow = null;

View File

@@ -1,24 +1,29 @@
using System.Collections.Generic; using System.Collections.Generic;
using FeedCenter.Accounts;
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<AccountTypeItem> AccountTypes => public static List<AccountTypeItem> AccountTypes =>
[ [
new() new()
{ {
Name = Properties.Resources.AccountTypeFever, Name = Properties.Resources.AccountTypeFever,
AccountType = AccountType.Fever AccountType = AccountType.Fever
}, },
//new() //new()
//{ //{
// Name = Properties.Resources.AccountTypeGoogleReader, // Name = Properties.Resources.AccountTypeGoogleReader,
// AccountType = AccountType.GoogleReader // AccountType = AccountType.GoogleReader
//} //}
]; new()
} {
Name = Properties.Resources.AccountTypeMiniflux,
AccountType = AccountType.Miniflux
}
];
} }

View File

@@ -2,6 +2,7 @@ using System;
using System.Globalization; using System.Globalization;
using System.Linq; using System.Linq;
using System.Windows.Data; using System.Windows.Data;
using FeedCenter.Accounts;
namespace FeedCenter.Options; namespace FeedCenter.Options;

View File

@@ -4,10 +4,12 @@
xmlns:properties="clr-namespace:FeedCenter.Properties" xmlns:properties="clr-namespace:FeedCenter.Properties"
xmlns:feedCenter="clr-namespace:FeedCenter" xmlns:feedCenter="clr-namespace:FeedCenter"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
d:DataContext="{d:DesignInstance Type=feedCenter:Account}" d:DataContext="{d:DesignInstance Type=accounts:Account}"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:mah="http://metro.mahapps.com/winfx/xaml/controls" xmlns:mah="http://metro.mahapps.com/winfx/xaml/controls"
xmlns:options="clr-namespace:FeedCenter.Options" xmlns:options="clr-namespace:FeedCenter.Options"
xmlns:feeds="clr-namespace:FeedCenter.Feeds"
xmlns:accounts="clr-namespace:FeedCenter.Accounts"
mc:Ignorable="d" mc:Ignorable="d"
Title="AccountWindow" Title="AccountWindow"
Height="350" Height="350"

View File

@@ -1,8 +1,8 @@
using ChrisKaczor.Wpf.Validation; using ChrisKaczor.Wpf.Validation;
using System;
using System.Linq; using System.Linq;
using System.Threading.Tasks;
using System.Windows; using System.Windows;
using System.Windows.Threading; using FeedCenter.Accounts;
namespace FeedCenter.Options; namespace FeedCenter.Options;
@@ -33,46 +33,46 @@ public partial class AccountWindow
return ShowDialog(); return ShowDialog();
} }
private void HandleOkayButtonClick(object sender, RoutedEventArgs e) private async void HandleOkayButtonClick(object sender, RoutedEventArgs e)
{ {
var transaction = _entities.BeginTransaction(); try
if (!this.IsValid(OptionsTabControl))
{ {
transaction.Rollback(); var transaction = _entities.BeginTransaction();
return;
}
if (_isNew) if (!this.IsValid(OptionsTabControl))
{
_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(() =>
{ {
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(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);
}
} }
} }

View File

@@ -7,6 +7,8 @@
xmlns:properties="clr-namespace:FeedCenter.Properties" xmlns:properties="clr-namespace:FeedCenter.Properties"
xmlns:controls="clr-namespace:ChrisKaczor.Wpf.Controls;assembly=ChrisKaczor.Wpf.Controls.Link" xmlns:controls="clr-namespace:ChrisKaczor.Wpf.Controls;assembly=ChrisKaczor.Wpf.Controls.Link"
xmlns:feedCenter="clr-namespace:FeedCenter" xmlns:feedCenter="clr-namespace:FeedCenter"
xmlns:feeds="clr-namespace:FeedCenter.Feeds"
xmlns:accounts="clr-namespace:FeedCenter.Accounts"
mc:Ignorable="d" mc:Ignorable="d"
d:DesignHeight="311" d:DesignHeight="311"
d:DesignWidth="425"> d:DesignWidth="425">
@@ -46,7 +48,7 @@
BorderBrush="{DynamicResource {x:Static SystemColors.ActiveBorderBrushKey}}" BorderBrush="{DynamicResource {x:Static SystemColors.ActiveBorderBrushKey}}"
AllowDrop="True" AllowDrop="True"
Background="{x:Null}" Background="{x:Null}"
d:DataContext="{d:DesignInstance feedCenter:Account }"> d:DataContext="{d:DesignInstance accounts:Account }">
<DataGrid.Columns> <DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Name}" <DataGridTextColumn Binding="{Binding Name}"
Header="{x:Static properties:Resources.AccountNameColumnHeader}" Header="{x:Static properties:Resources.AccountNameColumnHeader}"

View File

@@ -1,9 +1,9 @@
using System.ComponentModel; using System.ComponentModel;
using System.Linq;
using System.Windows; using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
using System.Windows.Data; using System.Windows.Data;
using System.Windows.Input; using System.Windows.Input;
using FeedCenter.Accounts;
namespace FeedCenter.Options; namespace FeedCenter.Options;
@@ -52,7 +52,7 @@ public partial class AccountsOptionsPanel
private void AddAccount() private void AddAccount()
{ {
var account = new Account(AccountType.Fever); var account = new Account(AccountType.Miniflux);
var accountWindow = new AccountWindow(_entities); var accountWindow = new AccountWindow(_entities);

View File

@@ -9,6 +9,7 @@
xmlns:controls="clr-namespace:ChrisKaczor.Wpf.Controls;assembly=ChrisKaczor.Wpf.Controls.Link" xmlns:controls="clr-namespace:ChrisKaczor.Wpf.Controls;assembly=ChrisKaczor.Wpf.Controls.Link"
xmlns:mah="http://metro.mahapps.com/winfx/xaml/controls" xmlns:mah="http://metro.mahapps.com/winfx/xaml/controls"
xmlns:options="clr-namespace:FeedCenter.Options" xmlns:options="clr-namespace:FeedCenter.Options"
xmlns:feeds="clr-namespace:FeedCenter.Feeds"
WindowStartupLocation="CenterOwner" WindowStartupLocation="CenterOwner"
Icon="/FeedCenter;component/Resources/Application.ico" Icon="/FeedCenter;component/Resources/Application.ico"
FocusManager.FocusedElement="{Binding ElementName=FeedLinkFilterText}"> FocusManager.FocusedElement="{Binding ElementName=FeedLinkFilterText}">
@@ -99,9 +100,9 @@
mah:TextBoxHelper.Watermark="{x:Static my:Resources.openLabel}" mah:TextBoxHelper.Watermark="{x:Static my:Resources.openLabel}"
IsEnabled="{Binding ElementName=OpenCheckBox, Path=IsChecked}"> IsEnabled="{Binding ElementName=OpenCheckBox, Path=IsChecked}">
<ComboBoxItem Content="{x:Static my:Resources.openAllMultipleToolbarButton}" <ComboBoxItem Content="{x:Static my:Resources.openAllMultipleToolbarButton}"
Tag="{x:Static feedCenter:MultipleOpenAction.IndividualPages}" /> Tag="{x:Static feeds:MultipleOpenAction.IndividualPages}" />
<ComboBoxItem Content="{x:Static my:Resources.openAllSingleToolbarButton}" <ComboBoxItem Content="{x:Static my:Resources.openAllSingleToolbarButton}"
Tag="{x:Static feedCenter:MultipleOpenAction.SinglePage}" /> Tag="{x:Static feeds:MultipleOpenAction.SinglePage}" />
</ComboBox> </ComboBox>
</Grid> </Grid>
<StackPanel Grid.Column="0" <StackPanel Grid.Column="0"

View File

@@ -4,6 +4,7 @@ using System.Linq;
using System.Windows; using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
using System.Windows.Data; using System.Windows.Data;
using FeedCenter.Feeds;
namespace FeedCenter.Options; namespace FeedCenter.Options;

View File

@@ -9,7 +9,8 @@
xmlns:controls="clr-namespace:ChrisKaczor.Wpf.Windows;assembly=ChrisKaczor.Wpf.Windows.ControlBox" xmlns:controls="clr-namespace:ChrisKaczor.Wpf.Windows;assembly=ChrisKaczor.Wpf.Windows.ControlBox"
xmlns:mah="http://metro.mahapps.com/winfx/xaml/controls" xmlns:mah="http://metro.mahapps.com/winfx/xaml/controls"
xmlns:options="clr-namespace:FeedCenter.Options" xmlns:options="clr-namespace:FeedCenter.Options"
d:DataContext="{d:DesignInstance Type=feedCenter:Category}" xmlns:feeds="clr-namespace:FeedCenter.Feeds"
d:DataContext="{d:DesignInstance Type=feeds:Category}"
Title="CategoryWindow" Title="CategoryWindow"
Width="300" Width="300"
ResizeMode="NoResize" ResizeMode="NoResize"

View File

@@ -1,5 +1,6 @@
using ChrisKaczor.Wpf.Validation; using ChrisKaczor.Wpf.Validation;
using System.Windows; using System.Windows;
using FeedCenter.Feeds;
namespace FeedCenter.Options; namespace FeedCenter.Options;

View File

@@ -1,6 +1,3 @@
namespace FeedCenter.Options namespace FeedCenter.Options;
{
public class CheckedFeedListItem : CheckedListItem<Feed> public class CheckedFeedListItem : CheckedListItem<Feeds.Feed>;
{
}
}

View File

@@ -4,10 +4,11 @@
xmlns:properties="clr-namespace:FeedCenter.Properties" xmlns:properties="clr-namespace:FeedCenter.Properties"
xmlns:feedCenter="clr-namespace:FeedCenter" xmlns:feedCenter="clr-namespace:FeedCenter"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:mah="http://metro.mahapps.com/winfx/xaml/controls" xmlns:mah="http://metro.mahapps.com/winfx/xaml/controls"
xmlns:options="clr-namespace:FeedCenter.Options" xmlns:options="clr-namespace:FeedCenter.Options"
xmlns:feeds="clr-namespace:FeedCenter.Feeds"
mc:Ignorable="d" mc:Ignorable="d"
Title="FeedWindow" Title="FeedWindow"
Height="350" Height="350"
@@ -90,9 +91,9 @@
mah:TextBoxHelper.UseFloatingWatermark="True" mah:TextBoxHelper.UseFloatingWatermark="True"
mah:TextBoxHelper.Watermark="{x:Static properties:Resources.openLabel}"> mah:TextBoxHelper.Watermark="{x:Static properties:Resources.openLabel}">
<ComboBoxItem Content="{x:Static properties:Resources.openAllSingleToolbarButton}" <ComboBoxItem Content="{x:Static properties:Resources.openAllSingleToolbarButton}"
Tag="{x:Static feedCenter:MultipleOpenAction.SinglePage}" /> Tag="{x:Static feeds:MultipleOpenAction.SinglePage}" />
<ComboBoxItem Content="{x:Static properties:Resources.openAllMultipleToolbarButton}" <ComboBoxItem Content="{x:Static properties:Resources.openAllMultipleToolbarButton}"
Tag="{x:Static feedCenter:MultipleOpenAction.IndividualPages}" /> Tag="{x:Static feeds:MultipleOpenAction.IndividualPages}" />
</ComboBox> </ComboBox>
<ComboBox Name="UserAgentComboBox" <ComboBox Name="UserAgentComboBox"
mah:TextBoxHelper.UseFloatingWatermark="True" mah:TextBoxHelper.UseFloatingWatermark="True"

View File

@@ -14,7 +14,7 @@ public partial class FeedWindow
InitializeComponent(); InitializeComponent();
} }
public bool? Display(Feed feed, Window owner) public bool? Display(Feeds.Feed feed, Window owner)
{ {
CategoryComboBox.ItemsSource = _entities.Categories; CategoryComboBox.ItemsSource = _entities.Categories;

View File

@@ -7,6 +7,7 @@
xmlns:properties="clr-namespace:FeedCenter.Properties" xmlns:properties="clr-namespace:FeedCenter.Properties"
xmlns:controls="clr-namespace:ChrisKaczor.Wpf.Controls;assembly=ChrisKaczor.Wpf.Controls.Link" xmlns:controls="clr-namespace:ChrisKaczor.Wpf.Controls;assembly=ChrisKaczor.Wpf.Controls.Link"
xmlns:feedCenter="clr-namespace:FeedCenter" xmlns:feedCenter="clr-namespace:FeedCenter"
xmlns:feeds="clr-namespace:FeedCenter.Feeds"
mc:Ignorable="d" mc:Ignorable="d"
d:DesignHeight="311" d:DesignHeight="311"
d:DesignWidth="425"> d:DesignWidth="425">
@@ -45,7 +46,7 @@
BorderBrush="{DynamicResource {x:Static SystemColors.ActiveBorderBrushKey}}" BorderBrush="{DynamicResource {x:Static SystemColors.ActiveBorderBrushKey}}"
AllowDrop="True" AllowDrop="True"
Background="{x:Null}" Background="{x:Null}"
d:DataContext="{d:DesignInstance feedCenter:Category }"> d:DataContext="{d:DesignInstance feeds:Category }">
<DataGrid.Columns> <DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Name}" <DataGridTextColumn Binding="{Binding Name}"
Header="{x:Static properties:Resources.CategoryNameColumnHeader}" Header="{x:Static properties:Resources.CategoryNameColumnHeader}"
@@ -80,7 +81,7 @@
BorderBrush="{DynamicResource {x:Static SystemColors.ActiveBorderBrushKey}}" BorderBrush="{DynamicResource {x:Static SystemColors.ActiveBorderBrushKey}}"
Background="{x:Null}" Background="{x:Null}"
SelectionChanged="HandleFeedDataGridSelectionChanged" SelectionChanged="HandleFeedDataGridSelectionChanged"
d:DataContext="{d:DesignInstance feedCenter:Feed }"> d:DataContext="{d:DesignInstance feeds:Feed }">
<DataGrid.Columns> <DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Name}" <DataGridTextColumn Binding="{Binding Name}"
Header="{x:Static properties:Resources.FeedNameColumnHeader}" Header="{x:Static properties:Resources.FeedNameColumnHeader}"

View File

@@ -7,6 +7,8 @@ using System.Windows.Controls;
using System.Windows.Data; using System.Windows.Data;
using System.Windows.Input; using System.Windows.Input;
using System.Xml; using System.Xml;
using FeedCenter.Accounts;
using FeedCenter.Feeds;
namespace FeedCenter.Options; namespace FeedCenter.Options;

View File

@@ -12,7 +12,7 @@ public class UserAgentItem
{ {
new UserAgentItem new UserAgentItem
{ {
Caption = Properties.Resources.ApplicationUserAgentCaption, Caption = Resources.ApplicationUserAgentCaption,
UserAgent = string.Empty UserAgent = string.Empty
}, },
new UserAgentItem new UserAgentItem

View File

@@ -25,5 +25,5 @@ using System.Windows;
[assembly: NeutralResourcesLanguage("en-US")] [assembly: NeutralResourcesLanguage("en-US")]
[assembly: ThemeInfo(ResourceDictionaryLocation.None, ResourceDictionaryLocation.SourceAssembly)] [assembly: ThemeInfo(ResourceDictionaryLocation.None, ResourceDictionaryLocation.SourceAssembly)]
[assembly: AssemblyVersion("1.1.0.0")] [assembly: AssemblyVersion("0.0.0.0")]
[assembly: AssemblyFileVersion("1.1.0.0")] [assembly: AssemblyFileVersion("0.0.0.0")]

View File

@@ -7,7 +7,7 @@
<PublishDir>bin\Release\publish\</PublishDir> <PublishDir>bin\Release\publish\</PublishDir>
<PublishProtocol>FileSystem</PublishProtocol> <PublishProtocol>FileSystem</PublishProtocol>
<_TargetId>Folder</_TargetId> <_TargetId>Folder</_TargetId>
<TargetFramework>net9.0-windows8.0</TargetFramework> <TargetFramework>net10.0-windows8.0</TargetFramework>
<RuntimeIdentifier>win-x64</RuntimeIdentifier> <RuntimeIdentifier>win-x64</RuntimeIdentifier>
<SelfContained>true</SelfContained> <SelfContained>true</SelfContained>
<PublishSingleFile>true</PublishSingleFile> <PublishSingleFile>true</PublishSingleFile>

View File

@@ -19,7 +19,7 @@ namespace FeedCenter.Properties {
// class via a tool like ResGen or Visual Studio. // class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen // To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project. // with the /str option, or rebuild your VS project.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "18.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
public class Resources { public class Resources {
@@ -141,6 +141,15 @@ namespace FeedCenter.Properties {
} }
} }
/// <summary>
/// Looks up a localized string similar to Miniflux.
/// </summary>
public static string AccountTypeMiniflux {
get {
return ResourceManager.GetString("AccountTypeMiniflux", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to URL. /// Looks up a localized string similar to URL.
/// </summary> /// </summary>

View File

@@ -614,5 +614,8 @@ All feeds currently in category "{0}" will be moved to the default category.</va
</data> </data>
<data name="includePrereleaseCheckBox" xml:space="preserve"> <data name="includePrereleaseCheckBox" xml:space="preserve">
<value>Include _prerelease</value> <value>Include _prerelease</value>
</data> </data>
<data name="AccountTypeMiniflux" xml:space="preserve">
<value>Miniflux</value>
</data>
</root> </root>

View File

@@ -10,7 +10,15 @@ assembly_info:
file: 'Properties\AssemblyInfo.cs' file: 'Properties\AssemblyInfo.cs'
assembly_version: "{version}" assembly_version: "{version}"
assembly_file_version: "{version}" assembly_file_version: "{version}"
environment:
DOTNET_NOLOGO: true
DOTNET_CLI_TELEMETRY_OPTOUT: 1
install:
- ps: |
Invoke-WebRequest -Uri 'https://dot.net/v1/dotnet-install.ps1' -UseBasicParsing -OutFile "$env:temp\dotnet-install.ps1"
& $env:temp\dotnet-install.ps1 -Architecture x64 -Version '10.0.100' -InstallDir "$env:ProgramFiles\dotnet"
before_build: before_build:
- ps: dotnet --version
- ps: nuget restore .\Installer\ - ps: nuget restore .\Installer\
- ps: dotnet publish .\Application\ /p:PublishProfile=Properties\PublishProfiles\x64.pubxml - ps: dotnet publish .\Application\ /p:PublishProfile=Properties\PublishProfiles\x64.pubxml
build: build: