mirror of
https://github.com/ckaczor/FeedCenter.git
synced 2026-02-16 18:47:28 -05:00
Start adding Miniflux support plus other cleanup
- Modernize old async code - Update to .NET 10 - Adjust namespace - Bypass update check when debugging
This commit is contained in:
@@ -1,10 +1,11 @@
|
||||
using Realms;
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Realms;
|
||||
|
||||
namespace FeedCenter;
|
||||
namespace FeedCenter.Feeds;
|
||||
|
||||
public class Account : RealmObject, INotifyDataErrorInfo
|
||||
{
|
||||
@@ -37,6 +38,7 @@ public class Account : RealmObject, INotifyDataErrorInfo
|
||||
{
|
||||
AccountType.Fever => 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<int> 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<AccountReadResult> 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;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
using System;
|
||||
|
||||
namespace FeedCenter;
|
||||
namespace FeedCenter.Feeds;
|
||||
|
||||
public class AccountReadInput(FeedCenterEntities entities, Guid? feedId, bool forceRead, Action incrementProgress)
|
||||
{
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace FeedCenter;
|
||||
namespace FeedCenter.Feeds;
|
||||
|
||||
public enum AccountReadResult
|
||||
{
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
namespace FeedCenter;
|
||||
namespace FeedCenter.Feeds;
|
||||
|
||||
public enum AccountType
|
||||
{
|
||||
Local,
|
||||
Fever,
|
||||
GoogleReader
|
||||
GoogleReader,
|
||||
Miniflux
|
||||
}
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -3,7 +3,7 @@ using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace FeedCenter;
|
||||
namespace FeedCenter.Feeds;
|
||||
|
||||
internal class DataErrorDictionary : Dictionary<string, List<string>>
|
||||
{
|
||||
|
||||
@@ -19,7 +19,7 @@ using JetBrains.Annotations;
|
||||
using Realms;
|
||||
using Serilog;
|
||||
|
||||
namespace FeedCenter;
|
||||
namespace FeedCenter.Feeds;
|
||||
|
||||
public partial class Feed : RealmObject, INotifyDataErrorInfo
|
||||
{
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace FeedCenter;
|
||||
namespace FeedCenter.Feeds;
|
||||
|
||||
public enum FeedReadResult
|
||||
{
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace FeedCenter;
|
||||
namespace FeedCenter.Feeds;
|
||||
|
||||
public enum FeedType
|
||||
{
|
||||
|
||||
@@ -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<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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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<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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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<int> GetProgressSteps(AccountReadInput accountReadInput);
|
||||
public Task<AccountReadResult> Read(AccountReadInput accountReadInput);
|
||||
public Task MarkFeedItemRead(string feedItemId);
|
||||
}
|
||||
@@ -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<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;
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
146
Application/Feeds/MinifluxReader.cs
Normal file
146
Application/Feeds/MinifluxReader.cs
Normal file
@@ -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<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);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace FeedCenter;
|
||||
namespace FeedCenter.Feeds;
|
||||
|
||||
public enum MultipleOpenAction
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user