Merge pull request #18 from zadjii-msft/dev/crutkas/hackerNews

Dev/crutkas/hacker news
This commit is contained in:
Clint Rutkas
2024-08-30 16:40:56 -07:00
committed by GitHub
11 changed files with 153 additions and 128 deletions

View File

@@ -0,0 +1,26 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Diagnostics;
using Microsoft.Windows.CommandPalette.Extensions.Helpers;
namespace HackerNewsExtension;
internal sealed class CommentAction : InvokableCommand
{
private readonly NewsPost _post;
internal CommentAction(NewsPost post)
{
_post = post;
Name = "Open comments";
Icon = new("\ue8f2"); // chat bubbles
}
public override ActionResult Invoke()
{
Process.Start(new ProcessStartInfo(_post.CommentsLink) { UseShellExecute = true });
return ActionResult.KeepOpen();
}
}

View File

@@ -3,124 +3,18 @@
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net.Http;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.WindowsRuntime;
using System.Text.Json;
using System.Threading.Tasks;
using System.Xml.Linq;
using ABI.System;
using Microsoft.UI;
using Microsoft.Windows.CommandPalette.Extensions;
using Microsoft.Windows.CommandPalette.Extensions.Helpers;
using Windows.Foundation;
using Windows.Storage.Streams;
namespace HackerNewsExtension;
internal sealed class NewsPost
{
internal string Title { get; init; } = "";
internal string Link { get; init; } = "";
internal string CommentsLink { get; init; } = "";
internal string Poster { get; init; } = "";
}
internal sealed class LinkAction : InvokableCommand {
private readonly NewsPost post;
internal LinkAction(NewsPost post)
{
this.post = post;
this.Name = "Open link";
this.Icon = new("\uE8A7");
}
public override ActionResult Invoke()
{
Process.Start(new ProcessStartInfo(post.Link) { UseShellExecute = true });
return ActionResult.KeepOpen();
}
}
internal sealed class CommentAction : InvokableCommand
{
private readonly NewsPost post;
internal CommentAction(NewsPost post)
{
this.post = post;
this.Name = "Open comments";
this.Icon = new("\ue8f2"); // chat bubbles
}
public override ActionResult Invoke()
{
Process.Start(new ProcessStartInfo(post.CommentsLink) { UseShellExecute = true });
return ActionResult.KeepOpen();
}
}
sealed class HackerNewsPage : ListPage {
public HackerNewsPage()
{
this.Icon = new("https://news.ycombinator.com/favicon.ico");
this.Name = "Hacker News";
}
private static async Task<List<NewsPost>> GetHackerNewsTopPosts()
{
var posts = new List<NewsPost>();
using (HttpClient client = new HttpClient())
{
var response = await client.GetStringAsync("https://news.ycombinator.com/rss");
var xdoc = XDocument.Parse(response);
var x = xdoc.Descendants("item").First();
posts = xdoc.Descendants("item")
.Take(20)
.Select(item => new NewsPost()
{
Title = item.Element("title")?.Value ?? "",
Link = item.Element("link")?.Value ?? "",
CommentsLink = item.Element("comments")?.Value ?? "",
}).ToList();
}
return posts;
}
public override ISection[] GetItems()
{
var t = DoGetItems();
t.ConfigureAwait(false);
return t.Result;
}
private async Task<ISection[]> DoGetItems()
{
List<NewsPost> items = await GetHackerNewsTopPosts();
this.Loading = false;
var s = new ListSection()
{
Title = "Posts",
Items = items.Select((post) => new ListItem(new LinkAction(post))
{
Title = post.Title,
Subtitle = post.Link,
MoreCommands = [
new CommandContextItem(new CommentAction(post))
]
}).ToArray()
};
return [ s ] ;
}
}
public class HackerNewsActionsProvider : ICommandProvider
{
public string DisplayName => $"Hacker News Commands";
public IconDataType Icon => new("");
private readonly IListItem[] _Actions = [
public IconDataType Icon => new(string.Empty);
private readonly IListItem[] _actions = [
new ListItem(new HackerNewsPage()),
];
@@ -128,9 +22,8 @@ public class HackerNewsActionsProvider : ICommandProvider
public void Dispose() => throw new NotImplementedException();
#pragma warning restore CA1816 // Dispose methods should call SuppressFinalize
public IListItem[] TopLevelCommands()
{
return _Actions;
return _actions;
}
}

View File

@@ -7,7 +7,6 @@
<PublishProfile>win-$(Platform).pubxml</PublishProfile>
<UseWinUI>false</UseWinUI>
<EnableMsixTooling>true</EnableMsixTooling>
<TreatWarningsAsErrors>False</TreatWarningsAsErrors>
</PropertyGroup>
<ItemGroup>

View File

@@ -0,0 +1,69 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Runtime.InteropServices.WindowsRuntime;
using System.Threading.Tasks;
using System.Xml.Linq;
using Microsoft.Windows.CommandPalette.Extensions;
using Microsoft.Windows.CommandPalette.Extensions.Helpers;
namespace HackerNewsExtension;
internal sealed class HackerNewsPage : ListPage
{
public HackerNewsPage()
{
Icon = new("https://news.ycombinator.com/favicon.ico");
Name = "Hacker News";
}
private static async Task<List<NewsPost>> GetHackerNewsTopPosts()
{
var posts = new List<NewsPost>();
using (HttpClient client = new HttpClient())
{
var response = await client.GetStringAsync("https://news.ycombinator.com/rss");
var xdoc = XDocument.Parse(response);
var x = xdoc.Descendants("item").First();
posts = xdoc.Descendants("item")
.Take(20)
.Select(item => new NewsPost()
{
Title = item.Element("title")?.Value ?? string.Empty,
Link = item.Element("link")?.Value ?? string.Empty,
CommentsLink = item.Element("comments")?.Value ?? string.Empty,
}).ToList();
}
return posts;
}
public override ISection[] GetItems()
{
var t = DoGetItems();
t.ConfigureAwait(false);
return t.Result;
}
private async Task<ISection[]> DoGetItems()
{
List<NewsPost> items = await GetHackerNewsTopPosts();
this.Loading = false;
var s = new ListSection()
{
Title = "Posts",
Items = items.Select((post) => new ListItem(new LinkAction(post))
{
Title = post.Title,
Subtitle = post.Link,
MoreCommands = [new CommandContextItem(new CommentAction(post))],
}).ToArray(),
};
return [s];
}
}

View File

@@ -0,0 +1,26 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Diagnostics;
using Microsoft.Windows.CommandPalette.Extensions.Helpers;
namespace HackerNewsExtension;
internal sealed class LinkAction : InvokableCommand
{
private readonly NewsPost _post;
internal LinkAction(NewsPost post)
{
this._post = post;
this.Name = "Open link";
this.Icon = new("\uE8A7");
}
public override ActionResult Invoke()
{
Process.Start(new ProcessStartInfo(_post.Link) { UseShellExecute = true });
return ActionResult.KeepOpen();
}
}

View File

@@ -0,0 +1,16 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
namespace HackerNewsExtension;
public sealed class NewsPost
{
public string Title { get; init; } = string.Empty;
public string Link { get; init; } = string.Empty;
public string CommentsLink { get; init; } = string.Empty;
public string Poster { get; init; } = string.Empty;
}

View File

@@ -15,16 +15,16 @@ public static class RuntimeHelper
{
get
{
// TODO! for whatever reason, when I ported this into the PT
// TODO: for whatever reason, when I ported this into the PT
// codebase, this no longer compiled. We're only ever using it for
// the hacked up settings and ignoring it anyways, so I'm leaving
// it commented out for now.
//
// it commented out for now.
//
// See also:
// * https://github.com/microsoft/win32metadata/commit/6fee67ba73bfe1b126ce524f7de8d367f0317715
// * https://github.com/microsoft/win32metadata/issues/1311
//uint length = 0;
//return PInvoke.GetCurrentPackageFullName(ref length, null) != WIN32_ERROR.APPMODEL_ERROR_NO_PACKAGE;
// uint length = 0;
// return PInvoke.GetCurrentPackageFullName(ref length, null) != WIN32_ERROR.APPMODEL_ERROR_NO_PACKAGE;
#pragma warning disable IDE0025 // Use expression body for property
return true;
#pragma warning restore IDE0025 // Use expression body for property

View File

@@ -4,7 +4,6 @@
<RootNamespace>Microsoft.CmdPal.Common</RootNamespace>
<Nullable>enable</Nullable>
<UseWinUI>true</UseWinUI>
<TreatWarningsAsErrors>False</TreatWarningsAsErrors>
</PropertyGroup>
<ItemGroup>

View File

@@ -11,8 +11,7 @@ namespace Microsoft.CmdPal.Common.Services;
public class FileService : IFileService
{
static readonly Encoding encoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false);
private static readonly Encoding _encoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false);
#pragma warning disable CS8603 // Possible null reference return.
public T Read<T>(string folderPath, string fileName)
@@ -36,7 +35,7 @@ public class FileService : IFileService
}
var fileContent = JsonSerializer.Serialize(content);
File.WriteAllText(Path.Combine(folderPath, fileName), fileContent, encoding);
File.WriteAllText(Path.Combine(folderPath, fileName), fileContent, _encoding);
}
public void Delete(string folderPath, string fileName)

View File

@@ -13,7 +13,6 @@ public interface IExtensionService
Task<IEnumerable<IExtensionWrapper>> GetInstalledExtensionsAsync(bool includeDisabledExtensions = false);
// Task<IEnumerable<string>> GetInstalledHomeWidgetPackageFamilyNamesAsync(bool includeDisabledExtensions = false);
Task<IEnumerable<IExtensionWrapper>> GetInstalledExtensionsAsync(Microsoft.Windows.CommandPalette.Extensions.ProviderType providerType, bool includeDisabledExtensions = false);
IExtensionWrapper? GetInstalledExtension(string extensionUniqueId);
@@ -32,5 +31,5 @@ public interface IExtensionService
///// </summary>
///// <param name="extension">The out of proc extension object</param>
///// <returns>True only if the extension was disabled. False otherwise.</returns>
//public Task<bool> DisableExtensionIfWindowsFeatureNotAvailable(IExtensionWrapper extension);
// public Task<bool> DisableExtensionIfWindowsFeatureNotAvailable(IExtensionWrapper extension);
}

View File

@@ -28,15 +28,14 @@ public class LocalSettingsService : ILocalSettingsService
private readonly string _applicationDataFolder;
private readonly string _localSettingsFile;
private Dictionary<string, object> _settings;
private bool _isInitialized;
private readonly bool _isMsix;
private Dictionary<string, object> _settings;
private bool _isInitialized;
public LocalSettingsService(IFileService fileService, IOptions<LocalSettingsOptions> options)
{
_isMsix = false; // RuntimeHelper.IsMSIX;
_isMsix = false; // RuntimeHelper.IsMSIX;
_fileService = fileService;
_options = options.Value;