├── art ├── settings.png ├── toolwindow.png ├── view-menu.png ├── context-menu.png └── full-screen.png ├── src └── DeveloperNews │ ├── Resources │ ├── Icon.png │ ├── Text.resx │ └── Text.Designer.cs │ ├── Settings │ ├── DialogPageProvider.cs │ ├── Options.cs │ ├── BaseOptionPage.cs │ └── BaseOptionModel.cs │ ├── Feeds │ ├── FeedInfo.cs │ ├── FeedDownloader.cs │ ├── FeedStore.cs │ └── FeedOrchestrator.cs │ ├── Properties │ └── AssemblyInfo.cs │ ├── ToolWindows │ ├── NewsWindow.cs │ ├── NewsItemTemplateSelector.cs │ ├── PostControl.xaml.cs │ ├── SettingsControl.xaml │ ├── SettingsControl.xaml.cs │ ├── NewsWindowControl.xaml.cs │ ├── PostViewModel.cs │ ├── NewsViewModel.cs │ ├── VsTheme.cs │ ├── PostControl.xaml │ └── NewsWindowControl.xaml │ ├── source.extension.cs │ ├── VSPackage.cs │ ├── VSPackage.vsct │ ├── Commands │ └── NewsWindowCommand.cs │ ├── source.extension.vsixmanifest │ ├── registry.pkgdef │ ├── StatusBarInjector.cs │ ├── DeveloperNewsPackage.cs │ └── DeveloperNews.csproj ├── test ├── .editorconfig └── DeveloperNews.Test │ ├── Properties │ └── AssemblyInfo.cs │ ├── DeveloperNews.Test.csproj │ ├── FeedDownloaderTest.cs │ └── FeedOrchestratorTest.cs ├── appveyor.yml ├── README.md ├── DeveloperNews.sln ├── .gitattributes ├── .editorconfig ├── .gitignore └── LICENSE /art/settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madskristensen/DeveloperNews/master/art/settings.png -------------------------------------------------------------------------------- /art/toolwindow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madskristensen/DeveloperNews/master/art/toolwindow.png -------------------------------------------------------------------------------- /art/view-menu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madskristensen/DeveloperNews/master/art/view-menu.png -------------------------------------------------------------------------------- /art/context-menu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madskristensen/DeveloperNews/master/art/context-menu.png -------------------------------------------------------------------------------- /art/full-screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madskristensen/DeveloperNews/master/art/full-screen.png -------------------------------------------------------------------------------- /src/DeveloperNews/Resources/Icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madskristensen/DeveloperNews/master/src/DeveloperNews/Resources/Icon.png -------------------------------------------------------------------------------- /test/.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome:http://EditorConfig.org 2 | 3 | [*.{cs,vb}] 4 | # Naming rules - async methods to be prefixed with Async 5 | dotnet_naming_rule.async_methods_must_end_with_async.severity = none -------------------------------------------------------------------------------- /src/DeveloperNews/Settings/DialogPageProvider.cs: -------------------------------------------------------------------------------- 1 | namespace DevNews 2 | { 3 | /// 4 | /// A provider for custom implementations. 5 | /// 6 | internal class DialogPageProvider 7 | { 8 | public class General : BaseOptionPage { } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/DeveloperNews/Settings/Options.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel; 3 | 4 | namespace DevNews 5 | { 6 | /// 7 | /// The options used by this extension. 8 | /// 9 | internal class Options : BaseOptionModel 10 | { 11 | [DefaultValue("")] 12 | [Browsable(false)] 13 | public string FeedSelection { get; set; } = ""; 14 | 15 | [Browsable(false)] 16 | public DateTime LastRead { get; set; } 17 | 18 | [Browsable(false)] 19 | public int UnreadPosts { get; set; } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/DeveloperNews/Feeds/FeedInfo.cs: -------------------------------------------------------------------------------- 1 | namespace DevNews 2 | { 3 | /// 4 | /// Represents all the meta data about a news feed. 5 | /// 6 | public class FeedInfo 7 | { 8 | public string Name { get; set; } 9 | public string Url { get; set; } 10 | public bool IsSelected { get; set; } 11 | 12 | public string DisplayName 13 | { 14 | get { return Name.TrimStart('!', '?'); } 15 | } 16 | 17 | public override string ToString() 18 | { 19 | return $"{Name}:{IsSelected}"; 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/DeveloperNews/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.InteropServices; 3 | using DevNews; 4 | 5 | [assembly: AssemblyTitle(Vsix.Name)] 6 | [assembly: AssemblyDescription(Vsix.Description)] 7 | [assembly: AssemblyConfiguration("")] 8 | [assembly: AssemblyCompany(Vsix.Author)] 9 | [assembly: AssemblyProduct(Vsix.Name)] 10 | [assembly: AssemblyCopyright(Vsix.Author)] 11 | [assembly: AssemblyTrademark("")] 12 | [assembly: AssemblyCulture("")] 13 | 14 | [assembly: ComVisible(false)] 15 | 16 | [assembly: AssemblyVersion(Vsix.Version)] 17 | [assembly: AssemblyFileVersion(Vsix.Version)] 18 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | image: Visual Studio 2022 2 | 3 | install: 4 | - ps: (new-object Net.WebClient).DownloadString("https://raw.github.com/madskristensen/ExtensionScripts/master/AppVeyor/vsix.ps1") | iex 5 | 6 | before_build: 7 | - ps: Vsix-IncrementVsixVersion | Vsix-UpdateBuildVersion 8 | - ps: Vsix-TokenReplacement src\DeveloperNews\source.extension.cs 'Version = "([0-9\\.]+)"' 'Version = "{version}"' 9 | 10 | build_script: 11 | - nuget restore -Verbosity quiet 12 | - msbuild /p:configuration=Release /p:DeployExtension=false /p:ZipPackageCompressionLevel=normal /v:m 13 | 14 | #test_script: 15 | # - dotnet test test/FeedManager.Test/FeedManager.Test.csproj 16 | 17 | after_test: 18 | - ps: Vsix-PushArtifacts | Vsix-PublishToGallery 19 | -------------------------------------------------------------------------------- /test/DeveloperNews.Test/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | [assembly: AssemblyTitle("DeveloperNews.Test")] 6 | [assembly: AssemblyDescription("")] 7 | [assembly: AssemblyConfiguration("")] 8 | [assembly: AssemblyCompany("")] 9 | [assembly: AssemblyProduct("DeveloperNews.Test")] 10 | [assembly: AssemblyCopyright("Copyright © 2020")] 11 | [assembly: AssemblyTrademark("")] 12 | [assembly: AssemblyCulture("")] 13 | 14 | [assembly: ComVisible(false)] 15 | 16 | [assembly: Guid("3048d43a-f7cb-4e0d-a91d-1be51eb8f32b")] 17 | 18 | // [assembly: AssemblyVersion("1.0.*")] 19 | [assembly: AssemblyVersion("1.0.0.0")] 20 | [assembly: AssemblyFileVersion("1.0.0.0")] 21 | -------------------------------------------------------------------------------- /src/DeveloperNews/ToolWindows/NewsWindow.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | using System.ServiceModel.Syndication; 4 | using DevNews.Resources; 5 | using Microsoft.VisualStudio.Imaging; 6 | using Microsoft.VisualStudio.Shell; 7 | 8 | namespace DevNews.ToolWindows 9 | { 10 | /// 11 | /// The tool window hosting the Developer News content. 12 | /// 13 | [Guid(PackageGuids.guidToolWindowString)] 14 | public class NewsWindow : ToolWindowPane 15 | { 16 | public NewsWindow(SyndicationFeed feed) : base(null) 17 | { 18 | BitmapImageMoniker = KnownMonikers.Dictionary; 19 | Caption = Text.WindowTitle; 20 | Content = new NewsWindowControl(feed); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/DeveloperNews/ToolWindows/NewsItemTemplateSelector.cs: -------------------------------------------------------------------------------- 1 | using System.Windows; 2 | using System.Windows.Controls; 3 | 4 | namespace DevNews.ToolWindows 5 | { 6 | /// 7 | /// Template selector for news items to distinguish between timeline headers and posts 8 | /// 9 | public class NewsItemTemplateSelector : DataTemplateSelector 10 | { 11 | public DataTemplate TimelineHeaderTemplate { get; set; } 12 | public DataTemplate PostTemplate { get; set; } 13 | 14 | public override DataTemplate SelectTemplate(object item, DependencyObject container) 15 | { 16 | if (item is TimelineHeaderViewModel) 17 | return TimelineHeaderTemplate; 18 | 19 | if (item is PostViewModel) 20 | return PostTemplate; 21 | 22 | return base.SelectTemplate(item, container); 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /src/DeveloperNews/source.extension.cs: -------------------------------------------------------------------------------- 1 | // ------------------------------------------------------------------------------ 2 | // 3 | // This file was generated by VSIX Synchronizer 4 | // 5 | // ------------------------------------------------------------------------------ 6 | namespace DevNews 7 | { 8 | internal sealed partial class Vsix 9 | { 10 | public const string Id = "36cfa8d9-bd14-4d32-a8a6-34133aa2309e"; 11 | public const string Name = "Developer News 2022"; 12 | public const string Description = @"Always stay up to date with developer news from the Visual Studio team and other sources right within Visual Studio or your default browser."; 13 | public const string Language = "en-US"; 14 | public const string Version = "1.0.900"; 15 | public const string Author = "Mads Kristensen"; 16 | public const string Tags = "news, rss, feed"; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/DeveloperNews/Settings/BaseOptionPage.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.Shell; 2 | 3 | namespace DevNews 4 | { 5 | /// 6 | /// A base class for a DialogPage to show in Tools -> Options. 7 | /// 8 | internal class BaseOptionPage : DialogPage where T : BaseOptionModel, new() 9 | { 10 | private readonly BaseOptionModel _model; 11 | 12 | public BaseOptionPage() 13 | { 14 | #pragma warning disable VSTHRD104 // Offer async methods 15 | _model = ThreadHelper.JoinableTaskFactory.Run(BaseOptionModel.CreateAsync); 16 | #pragma warning restore VSTHRD104 // Offer async methods 17 | } 18 | 19 | public override object AutomationObject => _model; 20 | 21 | public override void LoadSettingsFromStorage() 22 | { 23 | _model.Load(); 24 | } 25 | 26 | public override void SaveSettingsToStorage() 27 | { 28 | _model.Save(); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/DeveloperNews/ToolWindows/PostControl.xaml.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics; 2 | using System.Windows; 3 | using System.Windows.Controls; 4 | 5 | namespace DevNews.ToolWindows 6 | { 7 | public partial class PostControl : UserControl 8 | { 9 | public PostControl() 10 | { 11 | InitializeComponent(); 12 | } 13 | 14 | private PostViewModel ViewModel => DataContext as PostViewModel; 15 | 16 | private void PostClick(object sender, RoutedEventArgs e) 17 | { 18 | OpenInDefaultBrowserClick(this, null); 19 | } 20 | 21 | private void OpenInDefaultBrowserClick(object sender, RoutedEventArgs e) 22 | { 23 | if (!string.IsNullOrEmpty(ViewModel?.Url)) 24 | { 25 | Process.Start(ViewModel.Url); 26 | } 27 | } 28 | 29 | private void CopyUrlClick(object sender, RoutedEventArgs e) 30 | { 31 | if (!string.IsNullOrEmpty(ViewModel?.Url)) 32 | { 33 | Clipboard.SetText(ViewModel.Url); 34 | } 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/DeveloperNews/ToolWindows/SettingsControl.xaml: -------------------------------------------------------------------------------- 1 | 9 | 10 | 11 |