Start adding server support

This commit is contained in:
2025-09-24 21:08:59 -04:00
parent 9e2e7aabe8
commit 4e721efa55
47 changed files with 1652 additions and 266 deletions

View File

@@ -21,9 +21,8 @@ public partial class MainWindow
private void HandleNewFeed(string feedUrl)
{
// Create and configure the new feed
var feed = Feed.Create();
var feed = Feed.Create(_database);
feed.Source = feedUrl;
feed.CategoryId = _database.DefaultCategory.Id;
// Try to detect the feed type
var feedTypeResult = feed.DetectFeedType();
@@ -31,7 +30,7 @@ public partial class MainWindow
// If we can't figure it out it could be an HTML page
if (feedTypeResult.Item1 == FeedType.Unknown)
{
// Only check if the feed was able to be read - otherwise fall through and show the dialog
// Only check if the feed was read - otherwise fall through and show the dialog
if (feedTypeResult.Item2.Length > 0)
{
// Create and load an HTML document with the text
@@ -87,19 +86,11 @@ public partial class MainWindow
// Show a tip
NotificationIcon.ShowBalloonTip(string.Format(Properties.Resources.FeedAddedNotification, feed.Name), H.NotifyIcon.Core.NotificationIcon.Info);
_currentFeed = feed;
// Refresh the database to current settings
ResetDatabase();
// Re-initialize the feed display
DisplayFeed();
}
else
{
// Feed read failed - create a new feed window
var feedForm = new FeedWindow();
var feedForm = new FeedWindow(_database);
var dialogResult = feedForm.Display(feed, this);
@@ -109,14 +100,14 @@ public partial class MainWindow
// Add the feed to the feed table
_database.SaveChanges(() => _database.Feeds.Add(feed));
_currentFeed = feed;
// Refresh the database to current settings
ResetDatabase();
// Re-initialize the feed display
DisplayFeed();
}
_currentFeed = feed;
// Refresh the database to current settings
ResetDatabase();
// Re-initialize the feed display
DisplayFeed();
}
}

View File

@@ -2,6 +2,7 @@
using FeedCenter.Properties;
using System;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
@@ -30,7 +31,7 @@ public partial class MainWindow
}
}
private void HandleItemMouseUp(object sender, MouseButtonEventArgs e)
private async void HandleItemMouseUp(object sender, MouseButtonEventArgs e)
{
// Only handle the middle button
if (e.ChangedButton != MouseButton.Middle)
@@ -39,18 +40,14 @@ public partial class MainWindow
// Get the feed item
var feedItem = (FeedItem) ((ListBoxItem) sender).DataContext;
// The feed item has been read and is no longer new
_database.SaveChanges(() =>
{
feedItem.BeenRead = true;
feedItem.New = false;
});
// Remove the item from the list
LinkTextList.Items.Remove(feedItem);
// The feed item has been read and is no longer new
await feedItem.MarkAsRead(_database);
}
private void HandleItemMouseDoubleClick(object sender, MouseButtonEventArgs e)
private async void HandleItemMouseDoubleClick(object sender, MouseButtonEventArgs e)
{
// Get the feed item
var feedItem = (FeedItem) ((ListBoxItem) sender).DataContext;
@@ -59,15 +56,11 @@ public partial class MainWindow
if (!InstalledBrowser.OpenLink(Settings.Default.Browser, feedItem.Link))
return;
// The feed item has been read and is no longer new
_database.SaveChanges(() =>
{
feedItem.BeenRead = true;
feedItem.New = false;
});
// Remove the item from the list
LinkTextList.Items.Remove(feedItem);
// The feed item has been read and is no longer new
await feedItem.MarkAsRead(_database);
}
private void HandleFeedButtonClick(object sender, RoutedEventArgs e)

View File

@@ -34,13 +34,13 @@ public partial class MainWindow
}
}
private void SetProgressMode(bool value, int feedCount)
private void SetProgressMode(bool showProgress, int maximum)
{
// Refresh the progress bar if we need it
if (value)
if (showProgress)
{
FeedReadProgress.Value = 0;
FeedReadProgress.Maximum = feedCount + 2;
FeedReadProgress.Maximum = maximum + 2;
FeedReadProgress.Visibility = Visibility.Visible;
}
else
@@ -76,11 +76,13 @@ public partial class MainWindow
return;
// Don't read if there is nothing to read
if (!_database.Feeds.Any())
if (!_database.Accounts.Any())
return;
var progressSteps = _database.Accounts.Sum(a => a.GetProgressSteps(_database));
// Switch to progress mode
SetProgressMode(true, _database.Feeds.Count);
SetProgressMode(true, progressSteps);
// Create the input class
var workerInput = new FeedReadWorkerInput(forceRead);
@@ -138,36 +140,69 @@ public partial class MainWindow
var database = new FeedCenterEntities();
// Get the worker
var worker = (BackgroundWorker) sender;
var worker = (BackgroundWorker)sender;
// Get the input information
var workerInput = (FeedReadWorkerInput) e.Argument ?? new FeedReadWorkerInput();
var workerInput = (FeedReadWorkerInput)e.Argument ?? new FeedReadWorkerInput();
// Setup for progress
var currentProgress = 0;
// Create the list of feeds to read
var feedsToRead = new List<Feed>();
var accountsToRead = new List<Account>();
// If we have a single feed then add it to the list - otherwise add them all
// If we have a single feed then get the account for that feed
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));
var feed = database.Feeds.FirstOrDefault(f => f.Id == workerInput.FeedId);
if (feed != null)
accountsToRead.Add(feed.Account);
}
else
{
// Otherwise get all accounts
accountsToRead.AddRange(database.Accounts);
}
var incrementProgress = () =>
{
// Increment progress
currentProgress += 1;
// Report progress
worker.ReportProgress(currentProgress);
};
// Loop over each account and read it
foreach (var account in accountsToRead)
{
var accountReadInput = new AccountReadInput(database, workerInput.FeedId, workerInput.ForceRead, incrementProgress);
account.Read(accountReadInput);
}
//// 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;

View File

@@ -285,11 +285,13 @@
<MenuItem Header="{x:Static properties:Resources.lockWindowCheckBox}"
IsCheckable="True"
IsChecked="{Binding Source={x:Static properties:Settings.Default}, Path=WindowLocked}" />
<Separator />
<MenuItem Header="{x:Static properties:Resources.CurrentFeed}">
<MenuItem Header="{x:Static properties:Resources.EditMenu}"
<Separator Name="SettingsMenuSeparator" />
<MenuItem Name="CurrentFeedMenu" Header="{x:Static properties:Resources.CurrentFeed}">
<MenuItem Name="EditCurrentFeedMenuItem"
Header="{x:Static properties:Resources.EditMenu}"
Click="HandleEditCurrentFeedMenuItemClick" />
<MenuItem Header="{x:Static properties:Resources.DeleteMenu}"
<MenuItem Name="DeleteCurrentFeedMenuItem"
Header="{x:Static properties:Resources.DeleteMenu}"
Click="HandleDeleteCurrentFeedMenuItemClick" />
</MenuItem>
</ContextMenu>

View File

@@ -1,12 +1,12 @@
using ChrisKaczor.ApplicationUpdate;
using ChrisKaczor.Wpf.Application;
using FeedCenter.Data;
using FeedCenter.Properties;
using Serilog;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
@@ -39,19 +39,19 @@ public partial class MainWindow : IDisposable
base.OnSourceInitialized(e);
// Initialize the window
Initialize();
Initialize().ContinueWith(_ => { }, TaskScheduler.FromCurrentSynchronizationContext());
}
protected override async void OnClosed(EventArgs e)
protected override void OnClosed(EventArgs e)
{
base.OnClosed(e);
await SingleInstance.Stop();
SingleInstance.Stop().ContinueWith(_ => { }, TaskScheduler.FromCurrentSynchronizationContext());
}
public async void Initialize()
public async Task Initialize()
{
// Setup the update handler
// Set up the update handler
InitializeUpdate();
// Show the notification icon
@@ -72,8 +72,8 @@ public partial class MainWindow : IDisposable
_feedReadWorker.ProgressChanged += HandleFeedReadWorkerProgressChanged;
_feedReadWorker.RunWorkerCompleted += HandleFeedReadWorkerCompleted;
// Setup the database
_database = Database.Entities;
// Set up the database
_database = new FeedCenterEntities();
// Initialize the single instance listener
SingleInstance.MessageReceived += SingleInstance_MessageReceived;
@@ -151,7 +151,7 @@ public partial class MainWindow : IDisposable
var currentId = _currentFeed?.IsValid ?? false ? _currentFeed.Id : Guid.Empty;
// Create a new database object
_database.Refresh();
_database = new FeedCenterEntities();
_feedList = _currentCategory == null
? _database.Feeds.ToList()
@@ -188,7 +188,9 @@ public partial class MainWindow : IDisposable
private void UpdateToolbarButtonState()
{
// Cache the feed count to save (a little) time
var feedCount = Settings.Default.DisplayEmptyFeeds ? _feedList.Count() : _feedList.Count(x => x.Items.Any(y => !y.BeenRead));
var feedCount = Settings.Default.DisplayEmptyFeeds
? _feedList.Count()
: _feedList.Count(x => x.Items.Any(y => !y.BeenRead));
// Set button states
PreviousToolbarButton.IsEnabled = feedCount > 1;
@@ -200,6 +202,11 @@ public partial class MainWindow : IDisposable
FeedLabel.Visibility = feedCount == 0 ? Visibility.Hidden : Visibility.Visible;
FeedButton.Visibility = feedCount == 0 ? Visibility.Hidden : Visibility.Visible;
CategoryGrid.Visibility = _database.Categories.Count > 1 ? Visibility.Visible : Visibility.Collapsed;
EditCurrentFeedMenuItem.Visibility = _currentFeed?.Account.SupportsFeedEdit ?? false ? Visibility.Visible : Visibility.Collapsed;
DeleteCurrentFeedMenuItem.Visibility = _currentFeed?.Account.SupportsFeedDelete ?? false ? Visibility.Visible : Visibility.Collapsed;
CurrentFeedMenu.Visibility = EditCurrentFeedMenuItem.IsVisible || DeleteCurrentFeedMenuItem.IsVisible ? Visibility.Visible : Visibility.Collapsed;
SettingsMenuSeparator.Visibility = CurrentFeedMenu.Visibility;
}
private void InitializeDisplay()
@@ -405,16 +412,14 @@ public partial class MainWindow : IDisposable
}
UpdateOpenAllButton();
UpdateToolbarButtonState();
}
private void MarkAllItemsAsRead()
private async Task MarkAllItemsAsRead()
{
// Loop over all items and mark them as read
_database.SaveChanges(() =>
{
foreach (FeedItem feedItem in LinkTextList.Items)
feedItem.BeenRead = true;
});
foreach (FeedItem feedItem in LinkTextList.Items)
await feedItem.MarkAsRead(_database);
// Clear the list
LinkTextList.Items.Clear();

View File

@@ -1,12 +1,16 @@
using System.IO;
using ChrisKaczor.InstalledBrowsers;
using FeedCenter.Options;
using FeedCenter.Properties;
using System;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Web.UI;
using System.Windows;
using System.Windows.Controls;
using ChrisKaczor.InstalledBrowsers;
using FeedCenter.Options;
using FeedCenter.Properties;
using Serilog;
using Serilog.Events;
namespace FeedCenter;
@@ -22,7 +26,7 @@ public partial class MainWindow
NextFeed();
}
private void OpenAllFeedItemsIndividually()
private async Task OpenAllFeedItemsIndividually()
{
// Create a new list of feed items
var feedItems = (from FeedItem feedItem in LinkTextList.Items select feedItem).ToList();
@@ -40,7 +44,7 @@ public partial class MainWindow
if (InstalledBrowser.OpenLink(Settings.Default.Browser, feedItem.Link))
{
// Mark the feed as read
_database.SaveChanges(() => feedItem.BeenRead = true);
await feedItem.MarkAsRead(_database);
// Remove the item
LinkTextList.Items.Remove(feedItem);
@@ -71,9 +75,21 @@ public partial class MainWindow
UpdateErrorLink();
}
private void HandleMarkReadToolbarButtonClick(object sender, RoutedEventArgs e)
private static void HandleException(Exception exception)
{
MarkAllItemsAsRead();
Log.Logger.Write(LogEventLevel.Debug, exception, "");
}
private async void HandleMarkReadToolbarButtonClick(object sender, RoutedEventArgs e)
{
try
{
await MarkAllItemsAsRead();
}
catch (Exception exception)
{
HandleException(exception);
}
}
private void HandleShowErrorsButtonClick(object sender, RoutedEventArgs e)
@@ -108,35 +124,49 @@ public partial class MainWindow
ReadFeeds(true);
}
private void HandleOpenAllMenuItemClick(object sender, RoutedEventArgs e)
private async void HandleOpenAllMenuItemClick(object sender, RoutedEventArgs e)
{
var menuItem = (MenuItem) e.Source;
try
{
var menuItem = (MenuItem) e.Source;
if (Equals(menuItem, MenuOpenAllSinglePage))
OpenAllFeedItemsOnSinglePage();
else if (Equals(menuItem, MenuOpenAllMultiplePages))
OpenAllFeedItemsIndividually();
if (Equals(menuItem, MenuOpenAllSinglePage))
await OpenAllFeedItemsOnSinglePage();
else if (Equals(menuItem, MenuOpenAllMultiplePages))
await OpenAllFeedItemsIndividually();
}
catch (Exception exception)
{
HandleException(exception);
}
}
private void HandleOpenAllToolbarButtonClick(object sender, RoutedEventArgs e)
private async void HandleOpenAllToolbarButtonClick(object sender, RoutedEventArgs e)
{
var multipleOpenAction = _currentFeed.MultipleOpenAction;
switch (multipleOpenAction)
try
{
case MultipleOpenAction.IndividualPages:
OpenAllFeedItemsIndividually();
break;
case MultipleOpenAction.SinglePage:
OpenAllFeedItemsOnSinglePage();
break;
var multipleOpenAction = _currentFeed.MultipleOpenAction;
switch (multipleOpenAction)
{
case MultipleOpenAction.IndividualPages:
await OpenAllFeedItemsIndividually();
break;
case MultipleOpenAction.SinglePage:
await OpenAllFeedItemsOnSinglePage();
break;
}
}
catch (Exception exception)
{
HandleException(exception);
}
}
private void HandleEditCurrentFeedMenuItemClick(object sender, RoutedEventArgs e)
{
// Create a new feed window
var feedWindow = new FeedWindow();
var feedWindow = new FeedWindow(_database);
// Display the feed window and get the result
var result = feedWindow.Display(_currentFeed, this);
@@ -174,19 +204,19 @@ public partial class MainWindow
DisplayFeed();
}
private void OpenAllFeedItemsOnSinglePage()
private async Task OpenAllFeedItemsOnSinglePage()
{
var fileName = Path.GetTempFileName() + ".html";
TextWriter textWriter = new StreamWriter(fileName);
using (var htmlTextWriter = new HtmlTextWriter(textWriter))
await using (var htmlTextWriter = new HtmlTextWriter(textWriter))
{
htmlTextWriter.RenderBeginTag(HtmlTextWriterTag.Html);
htmlTextWriter.RenderBeginTag(HtmlTextWriterTag.Head);
htmlTextWriter.RenderBeginTag(HtmlTextWriterTag.Title);
htmlTextWriter.Write(_currentFeed.Title);
await htmlTextWriter.WriteAsync(_currentFeed.Title);
htmlTextWriter.RenderEndTag();
htmlTextWriter.AddAttribute("http-equiv", "Content-Type");
@@ -214,13 +244,13 @@ public partial class MainWindow
htmlTextWriter.AddAttribute(HtmlTextWriterAttribute.Href, item.Link);
htmlTextWriter.RenderBeginTag(HtmlTextWriterTag.A);
htmlTextWriter.Write(item.Title.Length == 0 ? item.Link : item.Title);
await htmlTextWriter.WriteAsync(item.Title.Length == 0 ? item.Link : item.Title);
htmlTextWriter.RenderEndTag();
htmlTextWriter.RenderBeginTag(HtmlTextWriterTag.Br);
htmlTextWriter.RenderEndTag();
htmlTextWriter.Write(item.Description);
await htmlTextWriter.WriteAsync(item.Description);
htmlTextWriter.RenderEndTag();
@@ -231,11 +261,11 @@ public partial class MainWindow
htmlTextWriter.RenderEndTag();
}
textWriter.Flush();
await textWriter.FlushAsync();
textWriter.Close();
InstalledBrowser.OpenLink(Settings.Default.Browser, fileName);
MarkAllItemsAsRead();
await MarkAllItemsAsRead();
}
}