mirror of
https://github.com/ckaczor/FeedCenter.git
synced 2026-01-13 17:22:48 -05:00
Start adding server support
This commit is contained in:
@@ -7,18 +7,5 @@ public static class Database
|
||||
public static string DatabaseFile { get; set; }
|
||||
public static string DatabasePath { get; set; }
|
||||
|
||||
public static FeedCenterEntities Entities { get; set; }
|
||||
|
||||
public static bool Exists => File.Exists(DatabaseFile);
|
||||
|
||||
public static bool Loaded { get; set; }
|
||||
|
||||
public static void Load()
|
||||
{
|
||||
if (Loaded) return;
|
||||
|
||||
Entities = new FeedCenterEntities();
|
||||
|
||||
Loaded = true;
|
||||
}
|
||||
}
|
||||
@@ -4,24 +4,17 @@ using System.Collections.Specialized;
|
||||
|
||||
namespace FeedCenter.Data;
|
||||
|
||||
public class RealmObservableCollection<T> : ObservableCollection<T> where T : IRealmObject
|
||||
public class RealmObservableCollection<T>(Realm realm) : ObservableCollection<T>(realm.All<T>()) where T : IRealmObject
|
||||
{
|
||||
private readonly Realm _realm;
|
||||
|
||||
public RealmObservableCollection(Realm realm) : base(realm.All<T>())
|
||||
{
|
||||
_realm = realm;
|
||||
}
|
||||
|
||||
protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
|
||||
{
|
||||
if (e.OldItems != null)
|
||||
foreach (T item in e.OldItems)
|
||||
_realm.Remove(item);
|
||||
realm.Remove(item);
|
||||
|
||||
if (e.NewItems != null)
|
||||
foreach (T item in e.NewItems)
|
||||
_realm.Add(item);
|
||||
realm.Add(item);
|
||||
|
||||
base.OnCollectionChanged(e);
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using FeedCenter.Data;
|
||||
using FeedCenter.Data;
|
||||
using FeedCenter.Options;
|
||||
using Realms;
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
namespace FeedCenter;
|
||||
|
||||
@@ -12,16 +12,52 @@ public class FeedCenterEntities
|
||||
{
|
||||
var realmConfiguration = new RealmConfiguration($"{Database.DatabaseFile}")
|
||||
{
|
||||
SchemaVersion = 1,
|
||||
SchemaVersion = 2,
|
||||
MigrationCallback = (migration, oldSchemaVersion) =>
|
||||
{
|
||||
if (oldSchemaVersion == 1)
|
||||
migration.NewRealm.Add(Account.CreateDefault());
|
||||
|
||||
var localAccount = migration.NewRealm.All<Account>().First(a => a.Type == AccountType.Local);
|
||||
|
||||
var newVersionCategories = migration.NewRealm.All<Category>();
|
||||
|
||||
foreach (var newVersionCategory in newVersionCategories)
|
||||
{
|
||||
switch (oldSchemaVersion)
|
||||
{
|
||||
case 1:
|
||||
newVersionCategory.Account = localAccount;
|
||||
newVersionCategory.RemoteId = null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
var newVersionFeeds = migration.NewRealm.All<Feed>();
|
||||
|
||||
foreach (var newVersionFeed in newVersionFeeds)
|
||||
{
|
||||
if (oldSchemaVersion == 0)
|
||||
switch (oldSchemaVersion)
|
||||
{
|
||||
newVersionFeed.UserAgent = null;
|
||||
case 0:
|
||||
newVersionFeed.UserAgent = null;
|
||||
break;
|
||||
case 1:
|
||||
newVersionFeed.Account = localAccount;
|
||||
newVersionFeed.RemoteId = null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
var newVersionFeedItems = migration.NewRealm.All<FeedItem>();
|
||||
|
||||
foreach (var newVersionFeedItem in newVersionFeedItems)
|
||||
{
|
||||
switch (oldSchemaVersion)
|
||||
{
|
||||
case 1:
|
||||
newVersionFeedItem.RemoteId = null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -29,13 +65,20 @@ public class FeedCenterEntities
|
||||
|
||||
RealmInstance = Realm.GetInstance(realmConfiguration);
|
||||
|
||||
Accounts = new RealmObservableCollection<Account>(RealmInstance);
|
||||
Settings = new RealmObservableCollection<Setting>(RealmInstance);
|
||||
Feeds = new RealmObservableCollection<Feed>(RealmInstance);
|
||||
Categories = new RealmObservableCollection<Category>(RealmInstance);
|
||||
|
||||
if (!Accounts.Any())
|
||||
{
|
||||
RealmInstance.Write(() => Accounts.Add(Account.CreateDefault()));
|
||||
}
|
||||
|
||||
if (!Categories.Any())
|
||||
{
|
||||
RealmInstance.Write(() => Categories.Add(Category.CreateDefault()));
|
||||
var localAccount = Accounts.First(a => a.Type == AccountType.Local);
|
||||
RealmInstance.Write(() => Categories.Add(Category.CreateDefault(localAccount)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,15 +89,16 @@ public class FeedCenterEntities
|
||||
get { return Categories.First(c => c.IsDefault); }
|
||||
}
|
||||
|
||||
public RealmObservableCollection<Feed> Feeds { get; private set; }
|
||||
private Realm RealmInstance { get; }
|
||||
public RealmObservableCollection<Setting> Settings { get; private set; }
|
||||
|
||||
public void Refresh()
|
||||
public Account LocalAccount
|
||||
{
|
||||
RealmInstance.Refresh();
|
||||
get { return Accounts.First(a => a.Type == AccountType.Local); }
|
||||
}
|
||||
|
||||
public RealmObservableCollection<Feed> Feeds { get; }
|
||||
public RealmObservableCollection<Account> Accounts { get; }
|
||||
private Realm RealmInstance { get; }
|
||||
public RealmObservableCollection<Setting> Settings { get; }
|
||||
|
||||
public void SaveChanges(Action action)
|
||||
{
|
||||
RealmInstance.Write(action);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net70-windows</TargetFramework>
|
||||
<TargetFramework>net8.0-windows7.0</TargetFramework>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||
<UseWindowsForms>false</UseWindowsForms>
|
||||
@@ -25,6 +25,7 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="ChrisKaczor.ApplicationUpdate" Version="1.0.5" />
|
||||
<PackageReference Include="ChrisKaczor.FeverClient" Version="1.0.1" />
|
||||
<PackageReference Include="ChrisKaczor.GenericSettingsProvider" Version="1.0.4" />
|
||||
<PackageReference Include="ChrisKaczor.InstalledBrowsers" Version="1.0.4" />
|
||||
<PackageReference Include="ChrisKaczor.Wpf.Application.SingleInstance" Version="1.0.5" />
|
||||
@@ -48,7 +49,7 @@
|
||||
<PackageReference Include="NameBasedGrid" Version="0.10.1">
|
||||
<NoWarn>NU1701</NoWarn>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Realm" Version="10.21.1" />
|
||||
<PackageReference Include="Realm" Version="20.1.0" />
|
||||
<PackageReference Include="Serilog" Version="2.12.0" />
|
||||
<PackageReference Include="Serilog.Enrichers.Thread" Version="3.1.0" />
|
||||
<PackageReference Include="Serilog.Sinks.Console" Version="4.1.0" />
|
||||
|
||||
@@ -5,7 +5,6 @@ using System.Windows;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Input;
|
||||
using ChrisKaczor.InstalledBrowsers;
|
||||
using FeedCenter.Data;
|
||||
using FeedCenter.Options;
|
||||
using FeedCenter.Properties;
|
||||
|
||||
@@ -14,6 +13,7 @@ namespace FeedCenter;
|
||||
public partial class FeedErrorWindow
|
||||
{
|
||||
private CollectionViewSource _collectionViewSource;
|
||||
private readonly FeedCenterEntities _entities = new();
|
||||
|
||||
public FeedErrorWindow()
|
||||
{
|
||||
@@ -23,7 +23,7 @@ public partial class FeedErrorWindow
|
||||
public void Display(Window owner)
|
||||
{
|
||||
// Create a view and sort it by name
|
||||
_collectionViewSource = new CollectionViewSource { Source = Database.Entities.Feeds };
|
||||
_collectionViewSource = new CollectionViewSource { Source = _entities.Feeds };
|
||||
_collectionViewSource.Filter += HandleCollectionViewSourceFilter;
|
||||
_collectionViewSource.SortDescriptions.Add(new SortDescription("Name", ListSortDirection.Ascending));
|
||||
|
||||
@@ -62,7 +62,7 @@ public partial class FeedErrorWindow
|
||||
|
||||
var feed = (Feed) FeedDataGrid.SelectedItem;
|
||||
|
||||
var feedWindow = new FeedWindow();
|
||||
var feedWindow = new FeedWindow(_entities);
|
||||
|
||||
feedWindow.Display(feed, GetWindow(this));
|
||||
}
|
||||
@@ -74,7 +74,7 @@ public partial class FeedErrorWindow
|
||||
|
||||
var feed = (Feed) FeedDataGrid.SelectedItem;
|
||||
|
||||
Database.Entities.SaveChanges(() => Database.Entities.Feeds.Remove(feed));
|
||||
_entities.SaveChanges(() => _entities.Feeds.Remove(feed));
|
||||
|
||||
SetFeedButtonStates();
|
||||
}
|
||||
@@ -118,8 +118,6 @@ public partial class FeedErrorWindow
|
||||
entities.SaveChanges(() => feed.Read(true));
|
||||
});
|
||||
|
||||
Database.Entities.Refresh();
|
||||
|
||||
var selectedIndex = FeedDataGrid.SelectedIndex;
|
||||
|
||||
_collectionViewSource.View.Refresh();
|
||||
|
||||
203
Application/Feeds/Account.cs
Normal file
203
Application/Feeds/Account.cs
Normal file
@@ -0,0 +1,203 @@
|
||||
using Realms;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
|
||||
namespace FeedCenter;
|
||||
|
||||
public class Account : RealmObject, INotifyDataErrorInfo
|
||||
{
|
||||
public const string DefaultName = "< Local >";
|
||||
|
||||
private readonly DataErrorDictionary _dataErrorDictionary;
|
||||
|
||||
public Account() : this(AccountType.Local)
|
||||
{
|
||||
}
|
||||
|
||||
public Account(AccountType type)
|
||||
{
|
||||
Type = type;
|
||||
|
||||
_dataErrorDictionary = new DataErrorDictionary();
|
||||
_dataErrorDictionary.ErrorsChanged += DataErrorDictionaryErrorsChanged;
|
||||
}
|
||||
|
||||
[PrimaryKey]
|
||||
public Guid Id { get; set; } = Guid.NewGuid();
|
||||
|
||||
public AccountType Type
|
||||
{
|
||||
get => Enum.TryParse(TypeRaw, out AccountType result) ? result : AccountType.Local;
|
||||
set => TypeRaw = value.ToString();
|
||||
}
|
||||
|
||||
public bool SupportsFeedEdit => Type switch
|
||||
{
|
||||
AccountType.Fever => false,
|
||||
AccountType.GoogleReader => false,
|
||||
AccountType.Local => true,
|
||||
_ => throw new NotSupportedException()
|
||||
};
|
||||
|
||||
public bool SupportsFeedDelete => Type switch
|
||||
{
|
||||
AccountType.Fever => false,
|
||||
AccountType.GoogleReader => false,
|
||||
AccountType.Local => true,
|
||||
_ => throw new NotSupportedException()
|
||||
};
|
||||
|
||||
private string TypeRaw { get; set; }
|
||||
|
||||
public string Name
|
||||
{
|
||||
get => RawName;
|
||||
set
|
||||
{
|
||||
RawName = value;
|
||||
|
||||
ValidateString(nameof(Name), RawName);
|
||||
RaisePropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
[MapTo("Name")]
|
||||
private string RawName { get; set; } = string.Empty;
|
||||
|
||||
public string Url
|
||||
{
|
||||
get => RawUrl;
|
||||
set
|
||||
{
|
||||
RawUrl = value;
|
||||
|
||||
ValidateString(nameof(Url), RawUrl);
|
||||
RaisePropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
[MapTo("Url")]
|
||||
public string RawUrl { get; set; }
|
||||
|
||||
public string Username
|
||||
{
|
||||
get => RawUsername;
|
||||
set
|
||||
{
|
||||
RawUsername = value;
|
||||
|
||||
if (!Authenticate)
|
||||
{
|
||||
_dataErrorDictionary.ClearErrors(nameof(Username));
|
||||
return;
|
||||
}
|
||||
|
||||
ValidateString(nameof(Username), RawUsername);
|
||||
RaisePropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
[MapTo("Username")]
|
||||
public string RawUsername { get; set; }
|
||||
|
||||
public string Password
|
||||
{
|
||||
get => RawPassword;
|
||||
set
|
||||
{
|
||||
RawPassword = value;
|
||||
|
||||
if (!Authenticate)
|
||||
{
|
||||
_dataErrorDictionary.ClearErrors(nameof(Password));
|
||||
return;
|
||||
}
|
||||
|
||||
ValidateString(nameof(Password), RawPassword);
|
||||
RaisePropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
[MapTo("Password")]
|
||||
public string RawPassword { get; set; }
|
||||
|
||||
public bool Authenticate { get; set; }
|
||||
|
||||
public bool Enabled { get; set; } = true;
|
||||
|
||||
public int CheckInterval { get; set; } = 60;
|
||||
|
||||
public DateTimeOffset LastChecked { get; set; }
|
||||
|
||||
public bool HasErrors => _dataErrorDictionary.Any();
|
||||
|
||||
public IEnumerable GetErrors(string propertyName)
|
||||
{
|
||||
return _dataErrorDictionary.GetErrors(propertyName);
|
||||
}
|
||||
|
||||
public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;
|
||||
|
||||
private void DataErrorDictionaryErrorsChanged(object sender, DataErrorsChangedEventArgs e)
|
||||
{
|
||||
ErrorsChanged?.Invoke(this, new DataErrorsChangedEventArgs(e.PropertyName));
|
||||
}
|
||||
|
||||
private void ValidateString(string propertyName, string value)
|
||||
{
|
||||
_dataErrorDictionary.ClearErrors(propertyName);
|
||||
|
||||
if (string.IsNullOrWhiteSpace(value))
|
||||
_dataErrorDictionary.AddError(propertyName, $"{propertyName} cannot be empty");
|
||||
}
|
||||
|
||||
public static Account CreateDefault()
|
||||
{
|
||||
return new Account { Name = DefaultName, Type = AccountType.Local };
|
||||
}
|
||||
|
||||
public int GetProgressSteps(FeedCenterEntities entities)
|
||||
{
|
||||
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),
|
||||
_ => throw new NotSupportedException()
|
||||
};
|
||||
|
||||
return progressSteps;
|
||||
}
|
||||
|
||||
public AccountReadResult Read(AccountReadInput accountReadInput)
|
||||
{
|
||||
// If not enabled then do nothing
|
||||
if (!Enabled)
|
||||
return AccountReadResult.NotEnabled;
|
||||
|
||||
// Check if we're forcing a read
|
||||
if (!accountReadInput.ForceRead)
|
||||
{
|
||||
// Figure out how long since we last checked
|
||||
var timeSpan = DateTimeOffset.Now - LastChecked;
|
||||
|
||||
// Check if we are due to read the feed
|
||||
if (timeSpan.TotalMinutes < CheckInterval)
|
||||
return AccountReadResult.NotDue;
|
||||
}
|
||||
|
||||
var accountReadResult = Type switch
|
||||
{
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
11
Application/Feeds/AccountReadInput.cs
Normal file
11
Application/Feeds/AccountReadInput.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
using System;
|
||||
|
||||
namespace FeedCenter;
|
||||
|
||||
public class AccountReadInput(FeedCenterEntities entities, Guid? feedId, bool forceRead, Action incrementProgress)
|
||||
{
|
||||
public FeedCenterEntities Entities { get; set; } = entities;
|
||||
public Guid? FeedId { get; set; } = feedId;
|
||||
public bool ForceRead { get; set; } = forceRead;
|
||||
public Action IncrementProgress { get; private set; } = incrementProgress ?? throw new ArgumentNullException(nameof(incrementProgress));
|
||||
}
|
||||
8
Application/Feeds/AccountReadResult.cs
Normal file
8
Application/Feeds/AccountReadResult.cs
Normal file
@@ -0,0 +1,8 @@
|
||||
namespace FeedCenter;
|
||||
|
||||
public enum AccountReadResult
|
||||
{
|
||||
Success,
|
||||
NotDue,
|
||||
NotEnabled
|
||||
}
|
||||
8
Application/Feeds/AccountType.cs
Normal file
8
Application/Feeds/AccountType.cs
Normal file
@@ -0,0 +1,8 @@
|
||||
namespace FeedCenter;
|
||||
|
||||
public enum AccountType
|
||||
{
|
||||
Local,
|
||||
Fever,
|
||||
GoogleReader
|
||||
}
|
||||
@@ -22,6 +22,10 @@ public class Category : RealmObject, INotifyDataErrorInfo
|
||||
[PrimaryKey]
|
||||
public Guid Id { get; set; } = Guid.NewGuid();
|
||||
|
||||
public string RemoteId { get; set; }
|
||||
|
||||
public Account Account { get; set; }
|
||||
|
||||
public bool IsDefault { get; internal set; }
|
||||
|
||||
public string Name
|
||||
@@ -56,9 +60,9 @@ public class Category : RealmObject, INotifyDataErrorInfo
|
||||
ErrorsChanged?.Invoke(this, new DataErrorsChangedEventArgs(e.PropertyName));
|
||||
}
|
||||
|
||||
public static Category CreateDefault()
|
||||
public static Category CreateDefault(Account account)
|
||||
{
|
||||
return new Category { Name = DefaultName, IsDefault = true };
|
||||
return new Category { Name = DefaultName, IsDefault = true, Account = account };
|
||||
}
|
||||
|
||||
private void ValidateName()
|
||||
|
||||
@@ -12,7 +12,6 @@ using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using ChrisKaczor.ApplicationUpdate;
|
||||
using FeedCenter.Data;
|
||||
using FeedCenter.FeedParsers;
|
||||
using FeedCenter.Properties;
|
||||
using FeedCenter.Xml;
|
||||
@@ -34,14 +33,16 @@ public partial class Feed : RealmObject, INotifyDataErrorInfo
|
||||
_dataErrorDictionary.ErrorsChanged += DataErrorDictionaryErrorsChanged;
|
||||
}
|
||||
|
||||
[PrimaryKey]
|
||||
public Guid Id { get; set; }
|
||||
|
||||
public string RemoteId { get; set; }
|
||||
public bool Authenticate { get; set; }
|
||||
public Guid CategoryId { get; set; }
|
||||
public int CheckInterval { get; set; } = 60;
|
||||
public string Description { get; set; }
|
||||
public bool Enabled { get; set; } = true;
|
||||
|
||||
[PrimaryKey]
|
||||
public Guid Id { get; set; }
|
||||
public Account Account { get; set; }
|
||||
|
||||
[UsedImplicitly]
|
||||
public IList<FeedItem> Items { get; }
|
||||
@@ -171,9 +172,9 @@ public partial class Feed : RealmObject, INotifyDataErrorInfo
|
||||
|
||||
public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;
|
||||
|
||||
public static Feed Create()
|
||||
public static Feed Create(FeedCenterEntities entities)
|
||||
{
|
||||
return new Feed { Id = Guid.NewGuid(), CategoryId = Database.Entities.DefaultCategory.Id };
|
||||
return new Feed { Id = Guid.NewGuid(), CategoryId = entities.DefaultCategory.Id, Account = entities.LocalAccount };
|
||||
}
|
||||
|
||||
private void DataErrorDictionaryErrorsChanged(object sender, DataErrorsChangedEventArgs e)
|
||||
@@ -215,7 +216,7 @@ public partial class Feed : RealmObject, INotifyDataErrorInfo
|
||||
break;
|
||||
}
|
||||
|
||||
// If the feed was successfully read and we have no last update timestamp - set the last update timestamp to now
|
||||
// If the feed was successfully read, and we have no last update timestamp - set the last update timestamp to now
|
||||
if (result == FeedReadResult.Success && LastUpdated == default)
|
||||
LastUpdated = DateTimeOffset.Now;
|
||||
|
||||
|
||||
@@ -1,25 +1,28 @@
|
||||
using System;
|
||||
using System.Text.RegularExpressions;
|
||||
using FeedCenter.Options;
|
||||
using FeedCenter.Options;
|
||||
using Realms;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FeedCenter;
|
||||
|
||||
public partial class FeedItem : RealmObject
|
||||
{
|
||||
[PrimaryKey]
|
||||
public Guid Id { get; set; }
|
||||
|
||||
public bool BeenRead { get; set; }
|
||||
public string Description { get; set; }
|
||||
public Guid FeedId { get; set; }
|
||||
public string Guid { get; set; }
|
||||
|
||||
[PrimaryKey]
|
||||
public Guid Id { get; set; }
|
||||
|
||||
public DateTimeOffset LastFound { get; set; }
|
||||
public string Link { get; set; }
|
||||
public bool New { get; set; }
|
||||
public int Sequence { get; set; }
|
||||
public string Title { get; set; }
|
||||
public string RemoteId { get; set; }
|
||||
|
||||
public static FeedItem Create()
|
||||
{
|
||||
@@ -42,7 +45,7 @@ public partial class FeedItem : RealmObject
|
||||
case MultipleLineDisplay.FirstLine:
|
||||
|
||||
// Find the first newline
|
||||
var newlineIndex = title.IndexOf("\n", StringComparison.Ordinal);
|
||||
var newlineIndex = title.IndexOf('\n', StringComparison.Ordinal);
|
||||
|
||||
// If a newline was found return everything before it
|
||||
if (newlineIndex > -1)
|
||||
@@ -70,6 +73,34 @@ public partial class FeedItem : RealmObject
|
||||
return title;
|
||||
}
|
||||
|
||||
public async Task MarkAsRead(FeedCenterEntities entities)
|
||||
{
|
||||
var feed = entities.Feeds.FirstOrDefault(f => f.Id == FeedId);
|
||||
|
||||
entities.SaveChanges(() =>
|
||||
{
|
||||
BeenRead = true;
|
||||
New = false;
|
||||
});
|
||||
|
||||
if (feed == null || feed.Account.Type == AccountType.Local)
|
||||
return;
|
||||
|
||||
switch (feed.Account.Type)
|
||||
{
|
||||
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")]
|
||||
private static partial Regex NewlineRegex();
|
||||
|
||||
|
||||
145
Application/Feeds/FeverReader.cs
Normal file
145
Application/Feeds/FeverReader.cs
Normal file
@@ -0,0 +1,145 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using ChrisKaczor.FeverClient;
|
||||
|
||||
namespace FeedCenter;
|
||||
|
||||
internal class FeverReader : IAccountReader
|
||||
{
|
||||
public int GetProgressSteps(FeedCenterEntities entities)
|
||||
{
|
||||
return 7;
|
||||
}
|
||||
|
||||
public AccountReadResult Read(Account account, AccountReadInput accountReadInput)
|
||||
{
|
||||
var checkTime = DateTimeOffset.UtcNow;
|
||||
|
||||
var apiKey = account.Authenticate ? GetApiKey(account) : string.Empty;
|
||||
|
||||
var feverClient = new FeverClient(account.Url, apiKey);
|
||||
|
||||
accountReadInput.IncrementProgress();
|
||||
|
||||
var feverFeeds = feverClient.GetFeeds().Result.ToList();
|
||||
|
||||
accountReadInput.IncrementProgress();
|
||||
|
||||
var allFeverFeedItems = feverClient.GetAllFeedItems().Result.ToList();
|
||||
|
||||
accountReadInput.IncrementProgress();
|
||||
|
||||
var transaction = accountReadInput.Entities.BeginTransaction();
|
||||
|
||||
foreach (var feverFeed in feverFeeds)
|
||||
{
|
||||
var feed = accountReadInput.Entities.Feeds.FirstOrDefault(f => f.RemoteId == feverFeed.Id.ToString() && f.Account.Id == account.Id);
|
||||
|
||||
if (feed == null)
|
||||
{
|
||||
feed = new Feed
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
RemoteId = feverFeed.Id.ToString(),
|
||||
Title = feverFeed.Title,
|
||||
Source = feverFeed.Url,
|
||||
Link = feverFeed.SiteUrl,
|
||||
Account = account,
|
||||
Name = feverFeed.Title,
|
||||
CategoryId = accountReadInput.Entities.DefaultCategory.Id,
|
||||
Enabled = true,
|
||||
CheckInterval = 0,
|
||||
};
|
||||
|
||||
accountReadInput.Entities.Feeds.Add(feed);
|
||||
}
|
||||
|
||||
feed.Name = feverFeed.Title;
|
||||
feed.Title = feverFeed.Title;
|
||||
feed.Link = feverFeed.SiteUrl;
|
||||
feed.Source = feverFeed.Url;
|
||||
feed.LastReadResult = FeedReadResult.Success;
|
||||
feed.LastChecked = checkTime;
|
||||
|
||||
accountReadInput.IncrementProgress();
|
||||
|
||||
var feverFeedItems = allFeverFeedItems
|
||||
.Where(f => f.FeedId == feverFeed.Id)
|
||||
.OrderByDescending(fi => fi.CreatedOnTime).ToList();
|
||||
|
||||
var sequence = 1;
|
||||
|
||||
foreach (var feverFeedItem in feverFeedItems)
|
||||
{
|
||||
var feedItem = feed.Items.FirstOrDefault(f => f.RemoteId == feverFeedItem.Id.ToString());
|
||||
|
||||
if (feedItem == null)
|
||||
{
|
||||
feedItem = new FeedItem
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
RemoteId = feverFeedItem.Id.ToString(),
|
||||
Title = feverFeedItem.Title,
|
||||
Link = feverFeedItem.Url,
|
||||
Description = feverFeedItem.Html,
|
||||
BeenRead = feverFeedItem.IsRead,
|
||||
FeedId = feed.Id,
|
||||
Guid = Guid.NewGuid().ToString(),
|
||||
Sequence = sequence++,
|
||||
};
|
||||
|
||||
feed.Items.Add(feedItem);
|
||||
}
|
||||
|
||||
feedItem.LastFound = checkTime;
|
||||
feedItem.BeenRead = feverFeedItem.IsRead;
|
||||
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 = accountReadInput.Entities.Feeds.Where(f => f.Account.Id == account.Id && f.LastChecked != checkTime).ToList();
|
||||
|
||||
foreach (var feedNotSeen in feedsNotSeen)
|
||||
{
|
||||
accountReadInput.Entities.Feeds.Remove(feedNotSeen);
|
||||
}
|
||||
|
||||
account.LastChecked = checkTime;
|
||||
|
||||
transaction.Commit();
|
||||
|
||||
accountReadInput.IncrementProgress();
|
||||
|
||||
return AccountReadResult.Success;
|
||||
}
|
||||
|
||||
public static async Task MarkFeedItemRead(Account account, string feedItemId)
|
||||
{
|
||||
var apiKey = account.Authenticate ? GetApiKey(account) : string.Empty;
|
||||
|
||||
var feverClient = new FeverClient(account.Url, apiKey);
|
||||
|
||||
await feverClient.MarkFeedItemAsRead(int.Parse(feedItemId));
|
||||
}
|
||||
|
||||
private static string GetApiKey(Account account)
|
||||
{
|
||||
var input = $"{account.Username}:{account.Password}";
|
||||
var hash = MD5.HashData(Encoding.UTF8.GetBytes(input));
|
||||
return BitConverter.ToString(hash).Replace("-", "").ToLowerInvariant();
|
||||
}
|
||||
}
|
||||
144
Application/Feeds/GoogleReaderReader.cs
Normal file
144
Application/Feeds/GoogleReaderReader.cs
Normal file
@@ -0,0 +1,144 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FeedCenter;
|
||||
|
||||
internal class GoogleReaderReader : IAccountReader
|
||||
{
|
||||
public int GetProgressSteps(FeedCenterEntities entities)
|
||||
{
|
||||
return 7;
|
||||
}
|
||||
|
||||
public AccountReadResult Read(Account account, AccountReadInput accountReadInput)
|
||||
{
|
||||
var checkTime = DateTimeOffset.UtcNow;
|
||||
|
||||
//var apiKey = account.Authenticate ? GetApiKey(account) : string.Empty;
|
||||
|
||||
//var feverClient = new FeverClient.FeverClient(account.Url, apiKey);
|
||||
|
||||
//accountReadInput.IncrementProgress();
|
||||
|
||||
//var feverFeeds = feverClient.GetFeeds().Result.ToList();
|
||||
|
||||
//accountReadInput.IncrementProgress();
|
||||
|
||||
//var allFeverFeedItems = feverClient.GetAllFeedItems().Result.ToList();
|
||||
|
||||
//accountReadInput.IncrementProgress();
|
||||
|
||||
var transaction = accountReadInput.Entities.BeginTransaction();
|
||||
|
||||
//foreach (var feverFeed in feverFeeds)
|
||||
//{
|
||||
// var feed = accountReadInput.Entities.Feeds.FirstOrDefault(f => f.RemoteId == feverFeed.Id.ToString() && f.Account.Id == account.Id);
|
||||
|
||||
// if (feed == null)
|
||||
// {
|
||||
// feed = new Feed
|
||||
// {
|
||||
// Id = Guid.NewGuid(),
|
||||
// RemoteId = feverFeed.Id.ToString(),
|
||||
// Title = feverFeed.Title,
|
||||
// Source = feverFeed.Url,
|
||||
// Link = feverFeed.SiteUrl,
|
||||
// Account = account,
|
||||
// Name = feverFeed.Title,
|
||||
// CategoryId = accountReadInput.Entities.DefaultCategory.Id,
|
||||
// Enabled = true,
|
||||
// CheckInterval = 0,
|
||||
// };
|
||||
|
||||
// accountReadInput.Entities.Feeds.Add(feed);
|
||||
// }
|
||||
|
||||
// feed.Name = feverFeed.Title;
|
||||
// feed.Title = feverFeed.Title;
|
||||
// feed.Link = feverFeed.SiteUrl;
|
||||
// feed.Source = feverFeed.Url;
|
||||
// feed.LastReadResult = FeedReadResult.Success;
|
||||
// feed.LastChecked = checkTime;
|
||||
|
||||
// accountReadInput.IncrementProgress();
|
||||
|
||||
// var feverFeedItems = allFeverFeedItems
|
||||
// .Where(f => f.FeedId == feverFeed.Id)
|
||||
// .OrderByDescending(fi => fi.CreatedOnTime).ToList();
|
||||
|
||||
// var sequence = 1;
|
||||
|
||||
// foreach (var feverFeedItem in feverFeedItems)
|
||||
// {
|
||||
// var feedItem = feed.Items.FirstOrDefault(f => f.RemoteId == feverFeedItem.Id.ToString());
|
||||
|
||||
// if (feedItem == null)
|
||||
// {
|
||||
// feedItem = new FeedItem
|
||||
// {
|
||||
// Id = Guid.NewGuid(),
|
||||
// RemoteId = feverFeedItem.Id.ToString(),
|
||||
// Title = feverFeedItem.Title,
|
||||
// Link = feverFeedItem.Url,
|
||||
// Description = feverFeedItem.Html,
|
||||
// BeenRead = feverFeedItem.IsRead,
|
||||
// FeedId = feed.Id,
|
||||
// Guid = Guid.NewGuid().ToString(),
|
||||
// Sequence = sequence++,
|
||||
// };
|
||||
|
||||
// feed.Items.Add(feedItem);
|
||||
// }
|
||||
|
||||
// feedItem.LastFound = checkTime;
|
||||
// feedItem.BeenRead = feverFeedItem.IsRead;
|
||||
// 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 = accountReadInput.Entities.Feeds.Where(f => f.Account.Id == account.Id && f.LastChecked != checkTime).ToList();
|
||||
|
||||
//foreach (var feedNotSeen in feedsNotSeen)
|
||||
//{
|
||||
// accountReadInput.Entities.Feeds.Remove(feedNotSeen);
|
||||
//}
|
||||
|
||||
account.LastChecked = checkTime;
|
||||
|
||||
transaction.Commit();
|
||||
|
||||
accountReadInput.IncrementProgress();
|
||||
|
||||
return AccountReadResult.Success;
|
||||
}
|
||||
|
||||
public static async Task MarkFeedItemRead(Account account, string feedItemId)
|
||||
{
|
||||
//var apiKey = account.Authenticate ? GetApiKey(account) : string.Empty;
|
||||
|
||||
//var feverClient = new FeverClient.FeverClient(account.Url, apiKey);
|
||||
|
||||
//await feverClient.MarkFeedItemAsRead(int.Parse(feedItemId));
|
||||
}
|
||||
|
||||
//private static string GetApiKey(Account account)
|
||||
//{
|
||||
// var input = $"{account.Username}:{account.Password}";
|
||||
// var hash = MD5.HashData(Encoding.UTF8.GetBytes(input));
|
||||
// return BitConverter.ToString(hash).Replace("-", "").ToLowerInvariant();
|
||||
//}
|
||||
}
|
||||
7
Application/Feeds/IAccountReader.cs
Normal file
7
Application/Feeds/IAccountReader.cs
Normal file
@@ -0,0 +1,7 @@
|
||||
namespace FeedCenter;
|
||||
|
||||
public interface IAccountReader
|
||||
{
|
||||
public int GetProgressSteps(FeedCenterEntities entities);
|
||||
public AccountReadResult Read(Account account, AccountReadInput accountReadInput);
|
||||
}
|
||||
42
Application/Feeds/LocalReader.cs
Normal file
42
Application/Feeds/LocalReader.cs
Normal file
@@ -0,0 +1,42 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace FeedCenter;
|
||||
|
||||
public class LocalReader : IAccountReader
|
||||
{
|
||||
public int GetProgressSteps(FeedCenterEntities entities)
|
||||
{
|
||||
var enabledFeedCount = entities.Feeds.Count(f => f.Account.Type == AccountType.Local && f.Enabled);
|
||||
|
||||
return enabledFeedCount;
|
||||
}
|
||||
|
||||
public AccountReadResult Read(Account account, AccountReadInput accountReadInput)
|
||||
{
|
||||
var checkTime = DateTimeOffset.UtcNow;
|
||||
|
||||
// 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 (accountReadInput.FeedId != null)
|
||||
feedsToRead.Add(accountReadInput.Entities.Feeds.First(feed => feed.Id == accountReadInput.FeedId));
|
||||
else
|
||||
feedsToRead.AddRange(accountReadInput.Entities.Feeds.Where(f => f.Account.Type == AccountType.Local));
|
||||
|
||||
// Loop over each feed and read it
|
||||
foreach (var feed in feedsToRead)
|
||||
{
|
||||
// Read the feed
|
||||
accountReadInput.Entities.SaveChanges(() => feed.Read(accountReadInput.ForceRead));
|
||||
|
||||
accountReadInput.IncrementProgress();
|
||||
}
|
||||
|
||||
accountReadInput.Entities.SaveChanges(() => account.LastChecked = checkTime);
|
||||
|
||||
return AccountReadResult.Success;
|
||||
}
|
||||
}
|
||||
@@ -5,13 +5,7 @@
|
||||
<xs:complexType>
|
||||
<xs:all>
|
||||
<xs:element name="Realm" minOccurs="0" maxOccurs="1">
|
||||
<xs:complexType>
|
||||
<xs:attribute name="DisableAnalytics" type="xs:boolean">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Disables anonymized usage information from being sent on build. Read more about what data is being collected and why here: https://github.com/realm/realm-dotnet/blob/main/Realm/Realm.Weaver/Analytics.cs</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
</xs:complexType>
|
||||
<xs:complexType></xs:complexType>
|
||||
</xs:element>
|
||||
</xs:all>
|
||||
<xs:attribute name="VerifyAssembly" type="xs:boolean">
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@ namespace FeedCenter.Options;
|
||||
|
||||
public partial class AboutOptionsPanel
|
||||
{
|
||||
public AboutOptionsPanel(Window parentWindow) : base(parentWindow)
|
||||
public AboutOptionsPanel(Window parentWindow, FeedCenterEntities entities) : base(parentWindow, entities)
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
24
Application/Options/AccountTypeItem.cs
Normal file
24
Application/Options/AccountTypeItem.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace FeedCenter.Options
|
||||
{
|
||||
public class AccountTypeItem
|
||||
{
|
||||
public AccountType AccountType { get; set; }
|
||||
public string Name { get; set; }
|
||||
|
||||
public static List<AccountTypeItem> AccountTypes =>
|
||||
[
|
||||
new()
|
||||
{
|
||||
Name = Properties.Resources.AccountTypeFever,
|
||||
AccountType = AccountType.Fever
|
||||
},
|
||||
//new()
|
||||
//{
|
||||
// Name = Properties.Resources.AccountTypeGoogleReader,
|
||||
// AccountType = AccountType.GoogleReader
|
||||
//}
|
||||
];
|
||||
}
|
||||
}
|
||||
22
Application/Options/AccountTypeToNameConverter.cs
Normal file
22
Application/Options/AccountTypeToNameConverter.cs
Normal file
@@ -0,0 +1,22 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Windows.Data;
|
||||
|
||||
namespace FeedCenter.Options;
|
||||
|
||||
public class AccountTypeToNameConverter : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
if (value is AccountType accountType)
|
||||
return AccountTypeItem.AccountTypes.First(at => at.AccountType == accountType).Name;
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
139
Application/Options/AccountWindow.xaml
Normal file
139
Application/Options/AccountWindow.xaml
Normal file
@@ -0,0 +1,139 @@
|
||||
<Window x:Class="FeedCenter.Options.AccountWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:properties="clr-namespace:FeedCenter.Properties"
|
||||
xmlns:feedCenter="clr-namespace:FeedCenter"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
d:DataContext="{d:DesignInstance Type=feedCenter:Account}"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:mah="http://metro.mahapps.com/winfx/xaml/controls"
|
||||
xmlns:options="clr-namespace:FeedCenter.Options"
|
||||
mc:Ignorable="d"
|
||||
Title="AccountWindow"
|
||||
Height="350"
|
||||
Width="450"
|
||||
WindowStartupLocation="CenterOwner"
|
||||
Icon="/FeedCenter;component/Resources/Application.ico"
|
||||
FocusManager.FocusedElement="{Binding ElementName=NameTextBox}">
|
||||
<Window.Resources>
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Controls.xaml" />
|
||||
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Fonts.xaml" />
|
||||
<ResourceDictionary
|
||||
Source="pack://application:,,,/MahApps.Metro;component/Styles/Controls.FlatButton.xaml" />
|
||||
<ResourceDictionary
|
||||
Source="pack://application:,,,/MahApps.Metro;component/Styles/Themes/light.cobalt.xaml" />
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
</ResourceDictionary>
|
||||
</Window.Resources>
|
||||
<Grid Margin="6">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TabControl Name="OptionsTabControl"
|
||||
Grid.Row="0"
|
||||
Grid.Column="0"
|
||||
mah:HeaderedControlHelper.HeaderFontSize="16"
|
||||
mah:TabControlHelper.Underlined="SelectedTabItem">
|
||||
<TabItem Header="{x:Static properties:Resources.generalTab}">
|
||||
<StackPanel Margin="0,4"
|
||||
options:Spacing.Vertical="8">
|
||||
<ComboBox Name="AccountTypeComboBox"
|
||||
mah:TextBoxHelper.UseFloatingWatermark="True"
|
||||
mah:TextBoxHelper.Watermark="{x:Static properties:Resources.accountTypeLabel}"
|
||||
DisplayMemberPath="Name"
|
||||
ItemsSource="{Binding Source={x:Static options:AccountTypeItem.AccountTypes}}"
|
||||
SelectedValuePath="AccountType"
|
||||
SelectedValue="{Binding Path=Type, UpdateSourceTrigger=Explicit, ValidatesOnExceptions=true}" />
|
||||
<TextBox Name="NameTextBox"
|
||||
mah:TextBoxHelper.UseFloatingWatermark="True"
|
||||
mah:TextBoxHelper.Watermark="{x:Static properties:Resources.accountNameLabel}"
|
||||
mah:TextBoxHelper.SelectAllOnFocus="True"
|
||||
Text="{Binding Path=Name, UpdateSourceTrigger=Explicit, ValidatesOnExceptions=True}" />
|
||||
<TextBox Name="UrlTextBox"
|
||||
mah:TextBoxHelper.UseFloatingWatermark="True"
|
||||
mah:TextBoxHelper.Watermark="{x:Static properties:Resources.accountUrlLabel}"
|
||||
mah:TextBoxHelper.SelectAllOnFocus="True"
|
||||
Text="{Binding Path=Url, UpdateSourceTrigger=Explicit, ValidatesOnExceptions=True}" />
|
||||
<CheckBox Name="ReadIntervalCheckBox"
|
||||
VerticalContentAlignment="Center"
|
||||
IsChecked="{Binding Path=Enabled, UpdateSourceTrigger=Explicit, ValidatesOnExceptions=True}">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<Label Content="{x:Static properties:Resources.accountReadIntervalPrefix}"
|
||||
HorizontalAlignment="Left"
|
||||
Margin="0,0,5,0"
|
||||
VerticalAlignment="Center"
|
||||
Padding="0" />
|
||||
<mah:NumericUpDown Width="100"
|
||||
Maximum="10080"
|
||||
Minimum="1"
|
||||
IsEnabled="{Binding ElementName=ReadIntervalCheckBox, Path=IsChecked}"
|
||||
Value="{Binding CheckInterval, UpdateSourceTrigger=Explicit, ValidatesOnExceptions=True}" />
|
||||
<Label Content="{x:Static properties:Resources.accountReadIntervalSuffix}"
|
||||
HorizontalAlignment="Left"
|
||||
Margin="5,0,0,0"
|
||||
VerticalAlignment="Center"
|
||||
Padding="0" />
|
||||
</StackPanel>
|
||||
</CheckBox>
|
||||
</StackPanel>
|
||||
</TabItem>
|
||||
<TabItem Header="{x:Static properties:Resources.authenticationTab}">
|
||||
<StackPanel Margin="0,4">
|
||||
<CheckBox Content="{x:Static properties:Resources.accountRequiresAuthenticationCheckBox}"
|
||||
Margin="0,0,0,4"
|
||||
Name="RequiresAuthenticationCheckBox"
|
||||
IsChecked="{Binding Path=Authenticate, UpdateSourceTrigger=Explicit, ValidatesOnExceptions=True}" />
|
||||
<TextBox Name="AuthenticationUserNameTextBox"
|
||||
Margin="25,0,0,4"
|
||||
IsEnabled="{Binding ElementName=RequiresAuthenticationCheckBox, Path=IsChecked}"
|
||||
mah:TextBoxHelper.UseFloatingWatermark="True"
|
||||
mah:TextBoxHelper.Watermark="{x:Static properties:Resources.authenticationUserNameLabel}"
|
||||
Text="{Binding Path=Username, UpdateSourceTrigger=Explicit, ValidatesOnExceptions=True}" />
|
||||
<PasswordBox Name="AuthenticationPasswordTextBox"
|
||||
Margin="25,0,0,8"
|
||||
Style="{StaticResource MahApps.Styles.PasswordBox.Button.Revealed}"
|
||||
mah:PasswordBoxBindingBehavior.Password="{Binding Password, UpdateSourceTrigger=Explicit, ValidatesOnDataErrors=True}"
|
||||
mah:TextBoxHelper.UseFloatingWatermark="True"
|
||||
mah:TextBoxHelper.Watermark="{x:Static properties:Resources.authenticationPasswordLabel}"
|
||||
IsEnabled="{Binding ElementName=RequiresAuthenticationCheckBox, Path=IsChecked}" />
|
||||
</StackPanel>
|
||||
</TabItem>
|
||||
</TabControl>
|
||||
<Grid Name="AccountReadProgress"
|
||||
Grid.Row="1"
|
||||
Height="20"
|
||||
Visibility="Collapsed">
|
||||
<ProgressBar Name="AccountReadProgressBar" />
|
||||
<TextBlock Text="Loading account..."
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center" />
|
||||
</Grid>
|
||||
<StackPanel
|
||||
Name="ButtonPanel"
|
||||
Grid.Column="0"
|
||||
Grid.Row="1"
|
||||
Orientation="Horizontal"
|
||||
Visibility="Visible"
|
||||
Margin="0,5,0,0"
|
||||
HorizontalAlignment="Right">
|
||||
<Button Content="{x:Static properties:Resources.OkayButton}"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Bottom"
|
||||
Width="75"
|
||||
Margin="0,0,5,0"
|
||||
IsDefault="True"
|
||||
Click="HandleOkayButtonClick" />
|
||||
<Button Content="{x:Static properties:Resources.CancelButton}"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Bottom"
|
||||
Width="75"
|
||||
IsCancel="True" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Window>
|
||||
78
Application/Options/AccountWindow.xaml.cs
Normal file
78
Application/Options/AccountWindow.xaml.cs
Normal file
@@ -0,0 +1,78 @@
|
||||
using ChrisKaczor.Wpf.Validation;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Threading;
|
||||
|
||||
namespace FeedCenter.Options;
|
||||
|
||||
public partial class AccountWindow
|
||||
{
|
||||
private Account _account;
|
||||
private bool _isNew;
|
||||
private readonly FeedCenterEntities _entities;
|
||||
|
||||
public AccountWindow(FeedCenterEntities entities)
|
||||
{
|
||||
_entities = entities;
|
||||
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
public bool? Display(Account account, Window owner, bool isNew)
|
||||
{
|
||||
_account = account;
|
||||
_isNew = isNew;
|
||||
|
||||
DataContext = account;
|
||||
|
||||
Title = isNew ? Properties.Resources.AccountWindowAdd : Properties.Resources.AccountWindowEdit;
|
||||
|
||||
Owner = owner;
|
||||
|
||||
return ShowDialog();
|
||||
}
|
||||
|
||||
private void HandleOkayButtonClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var transaction = _entities.BeginTransaction();
|
||||
|
||||
if (!this.IsValid(OptionsTabControl))
|
||||
{
|
||||
transaction.Rollback();
|
||||
return;
|
||||
}
|
||||
|
||||
if (_isNew)
|
||||
{
|
||||
_entities.Accounts.Add(_account);
|
||||
}
|
||||
|
||||
transaction.Commit();
|
||||
|
||||
var accountId = _account.Id;
|
||||
|
||||
AccountReadProgressBar.Value = 0;
|
||||
AccountReadProgressBar.Maximum = _account.GetProgressSteps(_entities) + 1;
|
||||
|
||||
AccountReadProgress.Visibility = Visibility.Visible;
|
||||
ButtonPanel.Visibility = Visibility.Collapsed;
|
||||
|
||||
var dispatcher = Dispatcher.CurrentDispatcher;
|
||||
|
||||
Task.Run(() =>
|
||||
{
|
||||
var entities = new FeedCenterEntities();
|
||||
var account = entities.Accounts.First(a => a.Id == accountId);
|
||||
var accountReadInput = new AccountReadInput(entities, null, true, () => dispatcher.Invoke(() => AccountReadProgressBar.Value++));
|
||||
account.Read(accountReadInput);
|
||||
|
||||
dispatcher.Invoke(() =>
|
||||
{
|
||||
DialogResult = true;
|
||||
|
||||
Close();
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
98
Application/Options/AccountsOptionsPanel.xaml
Normal file
98
Application/Options/AccountsOptionsPanel.xaml
Normal file
@@ -0,0 +1,98 @@
|
||||
<options:OptionsPanelBase x:Class="FeedCenter.Options.AccountsOptionsPanel"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:options="clr-namespace:FeedCenter.Options"
|
||||
xmlns:properties="clr-namespace:FeedCenter.Properties"
|
||||
xmlns:controls="clr-namespace:ChrisKaczor.Wpf.Controls;assembly=ChrisKaczor.Wpf.Controls.Link"
|
||||
xmlns:feedCenter="clr-namespace:FeedCenter"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="311"
|
||||
d:DesignWidth="425">
|
||||
<options:OptionsPanelBase.Resources>
|
||||
<ResourceDictionary>
|
||||
<options:AccountTypeToNameConverter x:Key="AccountTypeToNameConverter" />
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Controls.xaml" />
|
||||
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Fonts.xaml" />
|
||||
<ResourceDictionary
|
||||
Source="pack://application:,,,/MahApps.Metro;component/Styles/Controls.FlatButton.xaml" />
|
||||
<ResourceDictionary
|
||||
Source="pack://application:,,,/MahApps.Metro;component/Styles/Themes/light.cobalt.xaml" />
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
</ResourceDictionary>
|
||||
</options:OptionsPanelBase.Resources>
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<DataGrid Name="AccountDataGrid"
|
||||
SelectionChanged="HandleAccountDataGridSelectionChanged"
|
||||
Grid.Row="0"
|
||||
SelectionMode="Single"
|
||||
SelectionUnit="FullRow"
|
||||
Grid.Column="0"
|
||||
AutoGenerateColumns="False"
|
||||
GridLinesVisibility="None"
|
||||
CanUserResizeRows="False"
|
||||
IsReadOnly="True"
|
||||
HeadersVisibility="Column"
|
||||
BorderThickness="1,1,1,1"
|
||||
BorderBrush="{DynamicResource {x:Static SystemColors.ActiveBorderBrushKey}}"
|
||||
AllowDrop="True"
|
||||
Background="{x:Null}"
|
||||
d:DataContext="{d:DesignInstance feedCenter:Account }">
|
||||
<DataGrid.Columns>
|
||||
<DataGridTextColumn Binding="{Binding Name}"
|
||||
Header="{x:Static properties:Resources.AccountNameColumnHeader}"
|
||||
SortDirection="Ascending"
|
||||
Width="*" />
|
||||
<DataGridTextColumn Binding="{Binding Type, Converter={StaticResource AccountTypeToNameConverter}}"
|
||||
Header="{x:Static properties:Resources.AccountTypeColumnHeader}"
|
||||
Width="*" />
|
||||
</DataGrid.Columns>
|
||||
<DataGrid.RowStyle>
|
||||
<Style TargetType="DataGridRow"
|
||||
BasedOn="{StaticResource MahApps.Styles.DataGridRow}">
|
||||
<EventSetter Event="MouseDoubleClick"
|
||||
Handler="HandleAccountDataGridRowMouseDoubleClick" />
|
||||
</Style>
|
||||
</DataGrid.RowStyle>
|
||||
</DataGrid>
|
||||
<Border Grid.Column="0"
|
||||
Grid.Row="1"
|
||||
BorderThickness="1,0,1,1"
|
||||
BorderBrush="{DynamicResource {x:Static SystemColors.ActiveBorderBrushKey}}" />
|
||||
<Border Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
BorderThickness="1,0,1,1"
|
||||
BorderBrush="{DynamicResource {x:Static SystemColors.ActiveBorderBrushKey}}">
|
||||
<StackPanel Orientation="Horizontal"
|
||||
Background="{DynamicResource {x:Static SystemColors.ControlBrushKey}}">
|
||||
<controls:Link Name="AddAccountButton"
|
||||
Margin="2"
|
||||
Click="HandleAddAccountButtonClick"
|
||||
Text="{x:Static properties:Resources.AddLink}"
|
||||
ToolTip="{x:Static properties:Resources.AddAccountButton}">
|
||||
</controls:Link>
|
||||
<controls:Link Name="EditAccountButton"
|
||||
Margin="2"
|
||||
Click="HandleEditAccountButtonClick"
|
||||
Text="{x:Static properties:Resources.EditLink}"
|
||||
ToolTip="{x:Static properties:Resources.EditAccountButton}">
|
||||
</controls:Link>
|
||||
<controls:Link Name="DeleteAccountButton"
|
||||
Margin="2"
|
||||
Click="HandleDeleteAccountButtonClick"
|
||||
Text="{x:Static properties:Resources.DeleteLink}"
|
||||
ToolTip="{x:Static properties:Resources.DeleteAccountButton}">
|
||||
</controls:Link>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</Grid>
|
||||
</options:OptionsPanelBase>
|
||||
129
Application/Options/AccountsOptionsPanel.xaml.cs
Normal file
129
Application/Options/AccountsOptionsPanel.xaml.cs
Normal file
@@ -0,0 +1,129 @@
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Input;
|
||||
|
||||
namespace FeedCenter.Options;
|
||||
|
||||
public partial class AccountsOptionsPanel
|
||||
{
|
||||
private readonly FeedCenterEntities _entities;
|
||||
|
||||
public AccountsOptionsPanel(Window parentWindow, FeedCenterEntities entities) : base(parentWindow, entities)
|
||||
{
|
||||
_entities = entities;
|
||||
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
public override string CategoryName => Properties.Resources.optionCategoryAccounts;
|
||||
|
||||
public override void LoadPanel()
|
||||
{
|
||||
base.LoadPanel();
|
||||
|
||||
var collectionViewSource = new CollectionViewSource { Source = _entities.Accounts };
|
||||
collectionViewSource.SortDescriptions.Add(new SortDescription("Name", ListSortDirection.Ascending));
|
||||
collectionViewSource.IsLiveSortingRequested = true;
|
||||
collectionViewSource.View.Filter = item =>
|
||||
{
|
||||
if (item is not Account account)
|
||||
return false;
|
||||
|
||||
// Filter out local accounts
|
||||
return account.Type != AccountType.Local;
|
||||
};
|
||||
|
||||
AccountDataGrid.ItemsSource = collectionViewSource.View;
|
||||
AccountDataGrid.SelectedIndex = 0;
|
||||
|
||||
SetAccountButtonStates();
|
||||
}
|
||||
|
||||
private void SetAccountButtonStates()
|
||||
{
|
||||
AddAccountButton.IsEnabled = true;
|
||||
|
||||
EditAccountButton.IsEnabled = AccountDataGrid.SelectedItem != null;
|
||||
DeleteAccountButton.IsEnabled = AccountDataGrid.SelectedItem != null;
|
||||
}
|
||||
|
||||
private void AddAccount()
|
||||
{
|
||||
var account = new Account(AccountType.Fever);
|
||||
|
||||
var accountWindow = new AccountWindow(_entities);
|
||||
|
||||
var result = accountWindow.Display(account, Window.GetWindow(this), true);
|
||||
|
||||
if (!result.HasValue || !result.Value)
|
||||
return;
|
||||
|
||||
AccountDataGrid.SelectedItem = account;
|
||||
|
||||
SetAccountButtonStates();
|
||||
}
|
||||
|
||||
private void EditSelectedAccount()
|
||||
{
|
||||
if (AccountDataGrid.SelectedItem == null)
|
||||
return;
|
||||
|
||||
var account = (Account) AccountDataGrid.SelectedItem;
|
||||
|
||||
var accountWindow = new AccountWindow(_entities);
|
||||
|
||||
accountWindow.Display(account, Window.GetWindow(this), false);
|
||||
}
|
||||
|
||||
private void DeleteSelectedAccount()
|
||||
{
|
||||
var account = (Account) AccountDataGrid.SelectedItem;
|
||||
|
||||
if (MessageBox.Show(ParentWindow, string.Format(Properties.Resources.ConfirmDeleteAccount, account.Name),
|
||||
Properties.Resources.ConfirmDeleteTitle, MessageBoxButton.YesNo, MessageBoxImage.Question,
|
||||
MessageBoxResult.No) == MessageBoxResult.No)
|
||||
return;
|
||||
|
||||
var index = AccountDataGrid.SelectedIndex;
|
||||
|
||||
if (index == AccountDataGrid.Items.Count - 1)
|
||||
AccountDataGrid.SelectedIndex = index - 1;
|
||||
else
|
||||
AccountDataGrid.SelectedIndex = index + 1;
|
||||
|
||||
_entities.SaveChanges(() => _entities.Accounts.Remove(account));
|
||||
|
||||
SetAccountButtonStates();
|
||||
}
|
||||
|
||||
private void HandleAddAccountButtonClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
AddAccount();
|
||||
}
|
||||
|
||||
private void HandleEditAccountButtonClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
EditSelectedAccount();
|
||||
}
|
||||
|
||||
private void HandleDeleteAccountButtonClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
DeleteSelectedAccount();
|
||||
}
|
||||
|
||||
private void HandleAccountDataGridSelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
SetAccountButtonStates();
|
||||
}
|
||||
|
||||
private void HandleAccountDataGridRowMouseDoubleClick(object sender, MouseButtonEventArgs e)
|
||||
{
|
||||
if (!EditAccountButton.IsEnabled)
|
||||
return;
|
||||
|
||||
EditSelectedAccount();
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
using FeedCenter.Data;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Windows;
|
||||
@@ -12,9 +11,12 @@ public partial class BulkFeedWindow
|
||||
{
|
||||
private List<CheckedListItem<Feed>> _checkedListBoxItems;
|
||||
private CollectionViewSource _collectionViewSource;
|
||||
private readonly FeedCenterEntities _entities;
|
||||
|
||||
public BulkFeedWindow()
|
||||
public BulkFeedWindow(FeedCenterEntities entities)
|
||||
{
|
||||
_entities = entities;
|
||||
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
@@ -22,7 +24,7 @@ public partial class BulkFeedWindow
|
||||
{
|
||||
_checkedListBoxItems = new List<CheckedListItem<Feed>>();
|
||||
|
||||
foreach (var feed in Database.Entities.Feeds)
|
||||
foreach (var feed in _entities.Feeds)
|
||||
_checkedListBoxItems.Add(new CheckedListItem<Feed> { Item = feed });
|
||||
|
||||
_collectionViewSource = new CollectionViewSource { Source = _checkedListBoxItems };
|
||||
@@ -52,7 +54,7 @@ public partial class BulkFeedWindow
|
||||
|
||||
private void HandleOkButtonClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
Database.Entities.SaveChanges(() =>
|
||||
_entities.SaveChanges(() =>
|
||||
{
|
||||
foreach (var item in _checkedListBoxItems.Where(i => i.IsChecked))
|
||||
{
|
||||
|
||||
@@ -24,14 +24,17 @@
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Controls.xaml" />
|
||||
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Fonts.xaml" />
|
||||
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Controls.FlatButton.xaml" />
|
||||
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Themes/light.cobalt.xaml" />
|
||||
<ResourceDictionary
|
||||
Source="pack://application:,,,/MahApps.Metro;component/Styles/Controls.FlatButton.xaml" />
|
||||
<ResourceDictionary
|
||||
Source="pack://application:,,,/MahApps.Metro;component/Styles/Themes/light.cobalt.xaml" />
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
</ResourceDictionary>
|
||||
</Window.Resources>
|
||||
<StackPanel Margin="6"
|
||||
options:Spacing.Vertical="5">
|
||||
<TextBox mah:TextBoxHelper.UseFloatingWatermark="True"
|
||||
<TextBox Name="NameTextBox"
|
||||
mah:TextBoxHelper.UseFloatingWatermark="True"
|
||||
mah:TextBoxHelper.Watermark="{x:Static properties:Resources.categoryNameLabel}"
|
||||
mah:TextBoxHelper.SelectAllOnFocus="True"
|
||||
Text="{Binding Path=Name, UpdateSourceTrigger=Explicit, ValidatesOnExceptions=True}" />
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
using System.Windows;
|
||||
using ChrisKaczor.Wpf.Validation;
|
||||
using FeedCenter.Data;
|
||||
using ChrisKaczor.Wpf.Validation;
|
||||
using System.Windows;
|
||||
|
||||
namespace FeedCenter.Options;
|
||||
|
||||
public partial class CategoryWindow
|
||||
{
|
||||
public CategoryWindow()
|
||||
private readonly FeedCenterEntities _entities;
|
||||
|
||||
public CategoryWindow(FeedCenterEntities entities)
|
||||
{
|
||||
_entities = entities;
|
||||
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
@@ -30,7 +33,7 @@ public partial class CategoryWindow
|
||||
|
||||
private void HandleOkayButtonClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var transaction = Database.Entities.BeginTransaction();
|
||||
var transaction = _entities.BeginTransaction();
|
||||
|
||||
if (!this.IsValid())
|
||||
{
|
||||
@@ -39,7 +42,6 @@ public partial class CategoryWindow
|
||||
}
|
||||
|
||||
transaction.Commit();
|
||||
Database.Entities.Refresh();
|
||||
|
||||
// Dialog is good
|
||||
DialogResult = true;
|
||||
|
||||
@@ -5,7 +5,7 @@ namespace FeedCenter.Options;
|
||||
|
||||
public partial class DisplayOptionsPanel
|
||||
{
|
||||
public DisplayOptionsPanel(Window parentWindow) : base(parentWindow)
|
||||
public DisplayOptionsPanel(Window parentWindow, FeedCenterEntities entities) : base(parentWindow, entities)
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
@@ -1,19 +1,22 @@
|
||||
using ChrisKaczor.Wpf.Validation;
|
||||
using FeedCenter.Data;
|
||||
using System.Windows;
|
||||
|
||||
namespace FeedCenter.Options;
|
||||
|
||||
public partial class FeedWindow
|
||||
{
|
||||
public FeedWindow()
|
||||
private readonly FeedCenterEntities _entities;
|
||||
|
||||
public FeedWindow(FeedCenterEntities entities)
|
||||
{
|
||||
_entities = entities;
|
||||
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
public bool? Display(Feed feed, Window owner)
|
||||
{
|
||||
CategoryComboBox.ItemsSource = Database.Entities.Categories;
|
||||
CategoryComboBox.ItemsSource = _entities.Categories;
|
||||
|
||||
DataContext = feed;
|
||||
|
||||
@@ -26,7 +29,7 @@ public partial class FeedWindow
|
||||
|
||||
private void HandleOkayButtonClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var transaction = Database.Entities.BeginTransaction();
|
||||
var transaction = _entities.BeginTransaction();
|
||||
|
||||
if (!this.IsValid(OptionsTabControl))
|
||||
{
|
||||
@@ -35,7 +38,6 @@ public partial class FeedWindow
|
||||
}
|
||||
|
||||
transaction.Commit();
|
||||
Database.Entities.Refresh();
|
||||
|
||||
DialogResult = true;
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using FeedCenter.Data;
|
||||
using Microsoft.Win32;
|
||||
using Microsoft.Win32;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
@@ -14,9 +13,12 @@ namespace FeedCenter.Options;
|
||||
public partial class FeedsOptionsPanel
|
||||
{
|
||||
private CollectionViewSource _collectionViewSource;
|
||||
private readonly FeedCenterEntities _entities;
|
||||
|
||||
public FeedsOptionsPanel(Window parentWindow) : base(parentWindow)
|
||||
public FeedsOptionsPanel(Window parentWindow, FeedCenterEntities entities) : base(parentWindow, entities)
|
||||
{
|
||||
_entities = entities;
|
||||
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
@@ -26,7 +28,7 @@ public partial class FeedsOptionsPanel
|
||||
{
|
||||
base.LoadPanel();
|
||||
|
||||
var collectionViewSource = new CollectionViewSource { Source = Database.Entities.Categories };
|
||||
var collectionViewSource = new CollectionViewSource { Source = _entities.Categories };
|
||||
collectionViewSource.SortDescriptions.Add(new SortDescription("SortKey", ListSortDirection.Ascending));
|
||||
collectionViewSource.SortDescriptions.Add(new SortDescription("Name", ListSortDirection.Ascending));
|
||||
collectionViewSource.IsLiveSortingRequested = true;
|
||||
@@ -44,20 +46,20 @@ public partial class FeedsOptionsPanel
|
||||
|
||||
private void AddFeed()
|
||||
{
|
||||
var feed = Feed.Create();
|
||||
var feed = Feed.Create(_entities);
|
||||
|
||||
var category = (Category) CategoryDataGrid.SelectedItem;
|
||||
|
||||
feed.CategoryId = category.Id;
|
||||
|
||||
var feedWindow = new FeedWindow();
|
||||
var feedWindow = new FeedWindow(_entities);
|
||||
|
||||
var result = feedWindow.Display(feed, Window.GetWindow(this));
|
||||
|
||||
if (!result.HasValue || !result.Value)
|
||||
return;
|
||||
|
||||
Database.Entities.SaveChanges(() => Database.Entities.Feeds.Add(feed));
|
||||
_entities.SaveChanges(() => _entities.Feeds.Add(feed));
|
||||
|
||||
FeedDataGrid.SelectedItem = feed;
|
||||
|
||||
@@ -71,7 +73,7 @@ public partial class FeedsOptionsPanel
|
||||
|
||||
var feed = (Feed) FeedDataGrid.SelectedItem;
|
||||
|
||||
var feedWindow = new FeedWindow();
|
||||
var feedWindow = new FeedWindow(_entities);
|
||||
|
||||
feedWindow.Display(feed, Window.GetWindow(this));
|
||||
}
|
||||
@@ -86,7 +88,7 @@ public partial class FeedsOptionsPanel
|
||||
FeedDataGrid.SelectedItems.CopyTo(selectedItems, 0);
|
||||
|
||||
foreach (var feed in selectedItems)
|
||||
Database.Entities.SaveChanges(() => Database.Entities.Feeds.Remove(feed));
|
||||
_entities.SaveChanges(() => _entities.Feeds.Remove(feed));
|
||||
|
||||
SetFeedButtonStates();
|
||||
}
|
||||
@@ -116,7 +118,7 @@ public partial class FeedsOptionsPanel
|
||||
ExportFeeds();
|
||||
}
|
||||
|
||||
private static void ExportFeeds()
|
||||
private void ExportFeeds()
|
||||
{
|
||||
var saveFileDialog = new SaveFileDialog
|
||||
{
|
||||
@@ -143,7 +145,7 @@ public partial class FeedsOptionsPanel
|
||||
xmlWriter.WriteStartElement("opml");
|
||||
xmlWriter.WriteStartElement("body");
|
||||
|
||||
foreach (var feed in Database.Entities.Feeds.OrderBy(feed => feed.Name))
|
||||
foreach (var feed in _entities.Feeds.OrderBy(feed => feed.Name))
|
||||
{
|
||||
xmlWriter.WriteStartElement("outline");
|
||||
|
||||
@@ -162,7 +164,7 @@ public partial class FeedsOptionsPanel
|
||||
xmlWriter.Close();
|
||||
}
|
||||
|
||||
private static void ImportFeeds()
|
||||
private void ImportFeeds()
|
||||
{
|
||||
var openFileDialog = new OpenFileDialog
|
||||
{
|
||||
@@ -188,8 +190,7 @@ public partial class FeedsOptionsPanel
|
||||
|
||||
while (xmlReader.NodeType != XmlNodeType.EndElement)
|
||||
{
|
||||
var feed = Feed.Create();
|
||||
feed.CategoryId = Database.Entities.Categories.First(c => c.IsDefault).Id;
|
||||
var feed = Feed.Create(_entities);
|
||||
|
||||
while (xmlReader.MoveToNextAttribute())
|
||||
{
|
||||
@@ -218,7 +219,7 @@ public partial class FeedsOptionsPanel
|
||||
if (string.IsNullOrEmpty(feed.Name))
|
||||
feed.Name = feed.Title;
|
||||
|
||||
Database.Entities.Feeds.Add(feed);
|
||||
_entities.SaveChanges(() => _entities.Feeds.Add(feed));
|
||||
|
||||
xmlReader.MoveToElement();
|
||||
|
||||
@@ -242,23 +243,23 @@ public partial class FeedsOptionsPanel
|
||||
var selectedId = ((Category) CategoryDataGrid.SelectedItem).Id;
|
||||
|
||||
EditCategoryButton.IsEnabled = CategoryDataGrid.SelectedItem != null &&
|
||||
selectedId != Database.Entities.DefaultCategory.Id;
|
||||
selectedId != _entities.DefaultCategory.Id;
|
||||
DeleteCategoryButton.IsEnabled = CategoryDataGrid.SelectedItem != null &&
|
||||
selectedId != Database.Entities.DefaultCategory.Id;
|
||||
selectedId != _entities.DefaultCategory.Id;
|
||||
}
|
||||
|
||||
private void AddCategory()
|
||||
{
|
||||
var category = new Category();
|
||||
|
||||
var categoryWindow = new CategoryWindow();
|
||||
var categoryWindow = new CategoryWindow(_entities);
|
||||
|
||||
var result = categoryWindow.Display(category, Window.GetWindow(this));
|
||||
|
||||
if (!result.HasValue || !result.Value)
|
||||
return;
|
||||
|
||||
Database.Entities.SaveChanges(() => Database.Entities.Categories.Add(category));
|
||||
_entities.SaveChanges(() => _entities.Categories.Add(category));
|
||||
|
||||
CategoryDataGrid.SelectedItem = category;
|
||||
|
||||
@@ -272,7 +273,7 @@ public partial class FeedsOptionsPanel
|
||||
|
||||
var category = (Category) CategoryDataGrid.SelectedItem;
|
||||
|
||||
var categoryWindow = new CategoryWindow();
|
||||
var categoryWindow = new CategoryWindow(_entities);
|
||||
|
||||
categoryWindow.Display(category, Window.GetWindow(this));
|
||||
}
|
||||
@@ -284,10 +285,10 @@ public partial class FeedsOptionsPanel
|
||||
if (MessageBox.Show(ParentWindow, string.Format(Properties.Resources.ConfirmDeleteCategory, category.Name), Properties.Resources.ConfirmDeleteTitle, MessageBoxButton.YesNo, MessageBoxImage.Question, MessageBoxResult.No) == MessageBoxResult.No)
|
||||
return;
|
||||
|
||||
var defaultCategory = Database.Entities.DefaultCategory;
|
||||
var defaultCategory = _entities.DefaultCategory;
|
||||
|
||||
foreach (var feed in Database.Entities.Feeds.Where(f => f.CategoryId == category.Id))
|
||||
Database.Entities.SaveChanges(() => feed.CategoryId = defaultCategory.Id);
|
||||
foreach (var feed in _entities.Feeds.Where(f => f.CategoryId == category.Id))
|
||||
_entities.SaveChanges(() => feed.CategoryId = defaultCategory.Id);
|
||||
|
||||
var index = CategoryDataGrid.SelectedIndex;
|
||||
|
||||
@@ -296,7 +297,7 @@ public partial class FeedsOptionsPanel
|
||||
else
|
||||
CategoryDataGrid.SelectedIndex = index + 1;
|
||||
|
||||
Database.Entities.SaveChanges(() => Database.Entities.Categories.Remove(category));
|
||||
_entities.SaveChanges(() => _entities.Categories.Remove(category));
|
||||
|
||||
SetCategoryButtonStates();
|
||||
}
|
||||
@@ -320,7 +321,7 @@ public partial class FeedsOptionsPanel
|
||||
{
|
||||
if (_collectionViewSource == null)
|
||||
{
|
||||
_collectionViewSource = new CollectionViewSource { Source = Database.Entities.Feeds };
|
||||
_collectionViewSource = new CollectionViewSource { Source = _entities.Feeds };
|
||||
_collectionViewSource.SortDescriptions.Add(new SortDescription("Name", ListSortDirection.Ascending));
|
||||
_collectionViewSource.Filter += HandleCollectionViewSourceFilter;
|
||||
|
||||
@@ -342,7 +343,7 @@ public partial class FeedsOptionsPanel
|
||||
|
||||
var feed = (Feed) e.Item;
|
||||
|
||||
e.Accepted = feed.CategoryId == selectedCategory.Id;
|
||||
e.Accepted = feed.CategoryId == selectedCategory.Id && feed.Account.Type == AccountType.Local;
|
||||
}
|
||||
|
||||
private void HandleCategoryDataGridRowDrop(object sender, DragEventArgs e)
|
||||
@@ -352,7 +353,7 @@ public partial class FeedsOptionsPanel
|
||||
var category = (Category) ((DataGridRow) sender).Item;
|
||||
|
||||
foreach (var feed in feedList!)
|
||||
Database.Entities.SaveChanges(() => feed.CategoryId = category.Id);
|
||||
_entities.SaveChanges(() => feed.CategoryId = category.Id);
|
||||
|
||||
_collectionViewSource.View.Refresh();
|
||||
|
||||
@@ -382,7 +383,7 @@ public partial class FeedsOptionsPanel
|
||||
|
||||
private void HandleMultipleEditClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var bulkFeedWindow = new BulkFeedWindow();
|
||||
var bulkFeedWindow = new BulkFeedWindow(_entities);
|
||||
bulkFeedWindow.Display(Window.GetWindow(this));
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ namespace FeedCenter.Options;
|
||||
|
||||
public partial class GeneralOptionsPanel
|
||||
{
|
||||
public GeneralOptionsPanel(Window parentWindow) : base(parentWindow)
|
||||
public GeneralOptionsPanel(Window parentWindow, FeedCenterEntities entities) : base(parentWindow, entities)
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
@@ -6,10 +6,12 @@ namespace FeedCenter.Options;
|
||||
public class OptionsPanelBase : UserControl
|
||||
{
|
||||
protected readonly Window ParentWindow;
|
||||
protected readonly FeedCenterEntities Entities;
|
||||
|
||||
protected OptionsPanelBase(Window parentWindow)
|
||||
protected OptionsPanelBase(Window parentWindow, FeedCenterEntities entities)
|
||||
{
|
||||
ParentWindow = parentWindow;
|
||||
Entities = entities;
|
||||
}
|
||||
|
||||
public virtual string CategoryName => null;
|
||||
|
||||
@@ -6,6 +6,7 @@ namespace FeedCenter.Options;
|
||||
public partial class OptionsWindow
|
||||
{
|
||||
private readonly List<OptionsPanelBase> _optionPanels = new();
|
||||
private readonly FeedCenterEntities _entities = new();
|
||||
|
||||
public OptionsWindow()
|
||||
{
|
||||
@@ -20,11 +21,12 @@ public partial class OptionsWindow
|
||||
|
||||
private void AddCategories()
|
||||
{
|
||||
_optionPanels.Add(new GeneralOptionsPanel(this));
|
||||
_optionPanels.Add(new DisplayOptionsPanel(this));
|
||||
_optionPanels.Add(new FeedsOptionsPanel(this));
|
||||
_optionPanels.Add(new UpdateOptionsPanel(this));
|
||||
_optionPanels.Add(new AboutOptionsPanel(this));
|
||||
_optionPanels.Add(new GeneralOptionsPanel(this, _entities));
|
||||
_optionPanels.Add(new DisplayOptionsPanel(this, _entities));
|
||||
_optionPanels.Add(new FeedsOptionsPanel(this, _entities));
|
||||
_optionPanels.Add(new AccountsOptionsPanel(this, _entities));
|
||||
_optionPanels.Add(new UpdateOptionsPanel(this, _entities));
|
||||
_optionPanels.Add(new AboutOptionsPanel(this, _entities));
|
||||
}
|
||||
|
||||
private void LoadCategories()
|
||||
|
||||
@@ -6,7 +6,7 @@ namespace FeedCenter.Options;
|
||||
|
||||
public partial class UpdateOptionsPanel
|
||||
{
|
||||
public UpdateOptionsPanel(Window parentWindow) : base(parentWindow)
|
||||
public UpdateOptionsPanel(Window parentWindow, FeedCenterEntities entities) : base(parentWindow, entities)
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
155
Application/Properties/Resources.Designer.cs
generated
155
Application/Properties/Resources.Designer.cs
generated
@@ -60,6 +60,114 @@ namespace FeedCenter.Properties {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Name.
|
||||
/// </summary>
|
||||
public static string AccountNameColumnHeader {
|
||||
get {
|
||||
return ResourceManager.GetString("AccountNameColumnHeader", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Name.
|
||||
/// </summary>
|
||||
public static string accountNameLabel {
|
||||
get {
|
||||
return ResourceManager.GetString("accountNameLabel", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to _Refresh account every.
|
||||
/// </summary>
|
||||
public static string accountReadIntervalPrefix {
|
||||
get {
|
||||
return ResourceManager.GetString("accountReadIntervalPrefix", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to minutes.
|
||||
/// </summary>
|
||||
public static string accountReadIntervalSuffix {
|
||||
get {
|
||||
return ResourceManager.GetString("accountReadIntervalSuffix", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to _Account requires authentication.
|
||||
/// </summary>
|
||||
public static string accountRequiresAuthenticationCheckBox {
|
||||
get {
|
||||
return ResourceManager.GetString("accountRequiresAuthenticationCheckBox", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Type.
|
||||
/// </summary>
|
||||
public static string AccountTypeColumnHeader {
|
||||
get {
|
||||
return ResourceManager.GetString("AccountTypeColumnHeader", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Fever.
|
||||
/// </summary>
|
||||
public static string AccountTypeFever {
|
||||
get {
|
||||
return ResourceManager.GetString("AccountTypeFever", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Google Reader.
|
||||
/// </summary>
|
||||
public static string AccountTypeGoogleReader {
|
||||
get {
|
||||
return ResourceManager.GetString("AccountTypeGoogleReader", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Type.
|
||||
/// </summary>
|
||||
public static string accountTypeLabel {
|
||||
get {
|
||||
return ResourceManager.GetString("accountTypeLabel", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to URL.
|
||||
/// </summary>
|
||||
public static string accountUrlLabel {
|
||||
get {
|
||||
return ResourceManager.GetString("accountUrlLabel", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Add Account.
|
||||
/// </summary>
|
||||
public static string AccountWindowAdd {
|
||||
get {
|
||||
return ResourceManager.GetString("AccountWindowAdd", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Edit Account.
|
||||
/// </summary>
|
||||
public static string AccountWindowEdit {
|
||||
get {
|
||||
return ResourceManager.GetString("AccountWindowEdit", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to A_ctions.
|
||||
/// </summary>
|
||||
@@ -69,6 +177,15 @@ namespace FeedCenter.Properties {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Add Account.
|
||||
/// </summary>
|
||||
public static string AddAccountButton {
|
||||
get {
|
||||
return ResourceManager.GetString("AddAccountButton", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Add Category.
|
||||
/// </summary>
|
||||
@@ -277,6 +394,15 @@ namespace FeedCenter.Properties {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Are you sure you want to delete this account?.
|
||||
/// </summary>
|
||||
public static string ConfirmDeleteAccount {
|
||||
get {
|
||||
return ResourceManager.GetString("ConfirmDeleteAccount", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Are you sure you want to delete this category?
|
||||
///
|
||||
@@ -543,6 +669,15 @@ namespace FeedCenter.Properties {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Delete Account.
|
||||
/// </summary>
|
||||
public static string DeleteAccountButton {
|
||||
get {
|
||||
return ResourceManager.GetString("DeleteAccountButton", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Delete Category.
|
||||
/// </summary>
|
||||
@@ -597,6 +732,15 @@ namespace FeedCenter.Properties {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Edit Account.
|
||||
/// </summary>
|
||||
public static string EditAccountButton {
|
||||
get {
|
||||
return ResourceManager.GetString("EditAccountButton", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Edit Category.
|
||||
/// </summary>
|
||||
@@ -1155,6 +1299,15 @@ namespace FeedCenter.Properties {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Accounts.
|
||||
/// </summary>
|
||||
public static string optionCategoryAccounts {
|
||||
get {
|
||||
return ResourceManager.GetString("optionCategoryAccounts", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Categories.
|
||||
/// </summary>
|
||||
@@ -1174,7 +1327,7 @@ namespace FeedCenter.Properties {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Feeds.
|
||||
/// Looks up a localized string similar to Local Feeds.
|
||||
/// </summary>
|
||||
public static string optionCategoryFeeds {
|
||||
get {
|
||||
|
||||
@@ -230,7 +230,7 @@
|
||||
<value>About</value>
|
||||
</data>
|
||||
<data name="optionCategoryFeeds" xml:space="preserve">
|
||||
<value>Feeds</value>
|
||||
<value>Local Feeds</value>
|
||||
</data>
|
||||
<data name="optionCategoryUpdate" xml:space="preserve">
|
||||
<value>Update</value>
|
||||
@@ -561,4 +561,55 @@ All feeds currently in category "{0}" will be moved to the default category.</va
|
||||
<data name="FeedReadResult_TooManyRequests" xml:space="preserve">
|
||||
<value>Too many requests</value>
|
||||
</data>
|
||||
<data name="optionCategoryAccounts" xml:space="preserve">
|
||||
<value>Accounts</value>
|
||||
</data>
|
||||
<data name="AccountNameColumnHeader" xml:space="preserve">
|
||||
<value>Name</value>
|
||||
</data>
|
||||
<data name="AddAccountButton" xml:space="preserve">
|
||||
<value>Add Account</value>
|
||||
</data>
|
||||
<data name="EditAccountButton" xml:space="preserve">
|
||||
<value>Edit Account</value>
|
||||
</data>
|
||||
<data name="DeleteAccountButton" xml:space="preserve">
|
||||
<value>Delete Account</value>
|
||||
</data>
|
||||
<data name="AccountWindowAdd" xml:space="preserve">
|
||||
<value>Add Account</value>
|
||||
</data>
|
||||
<data name="AccountWindowEdit" xml:space="preserve">
|
||||
<value>Edit Account</value>
|
||||
</data>
|
||||
<data name="ConfirmDeleteAccount" xml:space="preserve">
|
||||
<value>Are you sure you want to delete this account?</value>
|
||||
</data>
|
||||
<data name="accountNameLabel" xml:space="preserve">
|
||||
<value>Name</value>
|
||||
</data>
|
||||
<data name="accountUrlLabel" xml:space="preserve">
|
||||
<value>URL</value>
|
||||
</data>
|
||||
<data name="accountReadIntervalPrefix" xml:space="preserve">
|
||||
<value>_Refresh account every</value>
|
||||
</data>
|
||||
<data name="accountReadIntervalSuffix" xml:space="preserve">
|
||||
<value>minutes</value>
|
||||
</data>
|
||||
<data name="accountRequiresAuthenticationCheckBox" xml:space="preserve">
|
||||
<value>_Account requires authentication</value>
|
||||
</data>
|
||||
<data name="accountTypeLabel" xml:space="preserve">
|
||||
<value>Type</value>
|
||||
</data>
|
||||
<data name="AccountTypeFever" xml:space="preserve">
|
||||
<value>Fever</value>
|
||||
</data>
|
||||
<data name="AccountTypeColumnHeader" xml:space="preserve">
|
||||
<value>Type</value>
|
||||
</data>
|
||||
<data name="AccountTypeGoogleReader" xml:space="preserve">
|
||||
<value>Google Reader</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -9,12 +9,7 @@ public static class SettingsStore
|
||||
{
|
||||
public static object OpenDataStore()
|
||||
{
|
||||
if (!Database.Exists)
|
||||
return null;
|
||||
|
||||
Database.Load();
|
||||
|
||||
return Database.Entities;
|
||||
return !Database.Exists ? null : new FeedCenterEntities();
|
||||
}
|
||||
|
||||
public static string GetSettingValue(object dataStore, string name, Version _)
|
||||
|
||||
@@ -11,20 +11,13 @@ namespace FeedCenter;
|
||||
|
||||
public partial class SplashWindow : IDisposable
|
||||
{
|
||||
private class ProgressStep
|
||||
private class ProgressStep(string key, string caption, ProgressStep.ProgressCallback callback)
|
||||
{
|
||||
public delegate bool ProgressCallback();
|
||||
|
||||
public readonly string Key;
|
||||
public readonly string Caption;
|
||||
public readonly ProgressCallback Callback;
|
||||
|
||||
public ProgressStep(string key, string caption, ProgressCallback callback)
|
||||
{
|
||||
Key = key;
|
||||
Caption = caption;
|
||||
Callback = callback;
|
||||
}
|
||||
public readonly string Key = key;
|
||||
public readonly string Caption = caption;
|
||||
public readonly ProgressCallback Callback = callback;
|
||||
}
|
||||
|
||||
private readonly List<ProgressStep> _progressSteps = new();
|
||||
@@ -180,12 +173,7 @@ public partial class SplashWindow : IDisposable
|
||||
|
||||
private bool LoadDatabase()
|
||||
{
|
||||
_dispatcher.Invoke(() =>
|
||||
{
|
||||
Database.Load();
|
||||
|
||||
Settings.Default.Reload();
|
||||
});
|
||||
_dispatcher.Invoke(() => Settings.Default.Reload());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.5.33414.496
|
||||
# Visual Studio Version 18
|
||||
VisualStudioVersion = 18.0.11018.127 d18.0
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FeedCenter", "Application\FeedCenter.csproj", "{BD3D12F2-DE23-4466-83B1-1EB617A877A4}"
|
||||
EndProject
|
||||
|
||||
@@ -3,4 +3,5 @@
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpKeepExistingMigration/@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_002ESettingsUpgrade_002EMigrateBlankLinesAroundFieldToBlankLinesAroundProperty/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
|
||||
<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>
|
||||
31
appveyor.yml
31
appveyor.yml
@@ -1,7 +1,4 @@
|
||||
version: 1.1.0.{build}
|
||||
branches:
|
||||
only:
|
||||
- main
|
||||
pull_requests:
|
||||
do_not_increment_build_number: true
|
||||
skip_tags: true
|
||||
@@ -19,8 +16,28 @@ build:
|
||||
artifacts:
|
||||
- path: Bootstrapper\bin\Release\FeedCenterSetup.exe
|
||||
name: Release
|
||||
deploy:
|
||||
- provider: Environment
|
||||
name: GitHub
|
||||
before_build:
|
||||
- cmd: nuget restore
|
||||
- cmd: nuget restore
|
||||
|
||||
for:
|
||||
-
|
||||
branches:
|
||||
only:
|
||||
- main
|
||||
|
||||
deploy:
|
||||
- provider: Environment
|
||||
name: GitHub
|
||||
prerelease: false
|
||||
|
||||
-
|
||||
branches:
|
||||
only:
|
||||
- prerelease
|
||||
|
||||
version: 1.2.0.{build}
|
||||
|
||||
deploy:
|
||||
- provider: Environment
|
||||
name: GitHub
|
||||
prerelease: true
|
||||
|
||||
Reference in New Issue
Block a user