diff --git a/Needlework.Net/Assets/bg-event-pass.jpg b/Needlework.Net/Assets/bg-event-pass.jpg new file mode 100644 index 0000000..3f2ba5b Binary files /dev/null and b/Needlework.Net/Assets/bg-event-pass.jpg differ diff --git a/Needlework.Net/Models/HextechDocsPost.cs b/Needlework.Net/Models/HextechDocsPost.cs new file mode 100644 index 0000000..2b464b0 --- /dev/null +++ b/Needlework.Net/Models/HextechDocsPost.cs @@ -0,0 +1,13 @@ +namespace Needlework.Net.Models +{ + public class HextechDocsPost + { + public required string Path { get; init; } + + public required string Title { get; init; } + + public required string Excerpt { get; init; } + + public string Url => $"https://hextechdocs.dev{Path}"; + } +} diff --git a/Needlework.Net/Needlework.Net.csproj b/Needlework.Net/Needlework.Net.csproj index abe7683..636e0c1 100644 --- a/Needlework.Net/Needlework.Net.csproj +++ b/Needlework.Net/Needlework.Net.csproj @@ -17,6 +17,7 @@ + diff --git a/Needlework.Net/Program.cs b/Needlework.Net/Program.cs index 2e600ad..84119d7 100644 --- a/Needlework.Net/Program.cs +++ b/Needlework.Net/Program.cs @@ -87,6 +87,7 @@ class Program // HOME locator.Register(() => new HomeView()); locator.Register(() => new LibraryView()); + locator.Register(() => new HextechDocsPostView()); // SCHEMAS locator.Register(() => new SchemasView()); locator.Register(() => new Views.Pages.Schemas.SchemaSearchDetailsView()); @@ -103,6 +104,7 @@ class Program builder.AddSingleton(); builder.AddSingleton(); builder.AddSingleton(); + builder.AddSingleton(); builder.AddSingleton(new FlurlClientCache() .Add("GithubClient", "https://api.github.com") .Add("GithubUserContentClient", "https://raw.githubusercontent.com") diff --git a/Needlework.Net/Services/HextechDocsPostService.cs b/Needlework.Net/Services/HextechDocsPostService.cs new file mode 100644 index 0000000..962d815 --- /dev/null +++ b/Needlework.Net/Services/HextechDocsPostService.cs @@ -0,0 +1,41 @@ +using AngleSharp; +using FastCache; +using Needlework.Net.Models; +using System; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace Needlework.Net.Services +{ + public class HextechDocsPostService + { + private readonly IBrowsingContext _context = BrowsingContext.New(Configuration.Default.WithDefaultLoader()); + + public async Task> GetPostsAsync() + { + if (Cached>.TryGet(nameof(GetPostsAsync), out var cached)) + { + return cached; + } + + var document = await _context.OpenAsync("https://hextechdocs.dev/tag/lcu/"); + var elements = document.QuerySelectorAll("article.post-card"); + var posts = new List(); + foreach (var element in elements) + { + var path = element.QuerySelector("a.post-card-content-link")!.GetAttribute("href")!; + var title = element.QuerySelector(".post-card-title")!.TextContent; + var excerpt = element.QuerySelector(".post-card-excerpt > p")!.TextContent; + var post = new HextechDocsPost() + { + Path = path, + Title = title, + Excerpt = excerpt, + }; + posts.Add(post); + } + + return cached.Save(posts, TimeSpan.FromMinutes(60)); + } + } +} diff --git a/Needlework.Net/ViewModels/Pages/Home/HextechDocsPostViewModel.cs b/Needlework.Net/ViewModels/Pages/Home/HextechDocsPostViewModel.cs new file mode 100644 index 0000000..ff0e674 --- /dev/null +++ b/Needlework.Net/ViewModels/Pages/Home/HextechDocsPostViewModel.cs @@ -0,0 +1,15 @@ +using CommunityToolkit.Mvvm.ComponentModel; +using Needlework.Net.Models; + +namespace Needlework.Net.ViewModels.Pages.Home +{ + public partial class HextechDocsPostViewModel : ObservableObject + { + public HextechDocsPostViewModel(HextechDocsPost hextechDocsPost) + { + HextechDocsPost = hextechDocsPost; + } + + public HextechDocsPost HextechDocsPost { get; } + } +} diff --git a/Needlework.Net/ViewModels/Pages/Home/HomeViewModel.cs b/Needlework.Net/ViewModels/Pages/Home/HomeViewModel.cs index 0b262ae..bf32152 100644 --- a/Needlework.Net/ViewModels/Pages/Home/HomeViewModel.cs +++ b/Needlework.Net/ViewModels/Pages/Home/HomeViewModel.cs @@ -1,18 +1,44 @@ using Avalonia; using Avalonia.Platform; +using Avalonia.Threading; using CommunityToolkit.Mvvm.ComponentModel; +using Needlework.Net.Extensions; using Needlework.Net.Models; +using Needlework.Net.Services; using System; using System.Collections.Generic; using System.Linq; +using System.Reactive; +using System.Reactive.Linq; using System.Text.Json; using System.Threading.Tasks; namespace Needlework.Net.ViewModels.Pages.Home; -public partial class HomeViewModel : PageBase +public partial class HomeViewModel : PageBase, IEnableLogger { - public HomeViewModel() : base("Home", "fa-solid fa-house", int.MinValue) { } + private readonly HextechDocsPostService _hextechDocsPostService; + + private readonly IDisposable _carouselNextDisposable; + + public HomeViewModel(HextechDocsPostService hextechDocsPostService) : base("Home", "fa-solid fa-house", int.MinValue) + { + _hextechDocsPostService = hextechDocsPostService; + + _carouselNextDisposable = Observable.Timer(TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(5)) + .Select(time => Unit.Default) + .Subscribe(_ => + { + if (SelectedHextechDocsPostsIndex == HextechDocsPosts.Count - 1) + { + SelectedHextechDocsPostsIndex = 0; + } + else + { + SelectedHextechDocsPostsIndex += 1; + } + }); + } public List Libraries { get; } = JsonSerializer.Deserialize>(AssetLoader.Open(new Uri($"avares://NeedleworkDotNet/Assets/libraries.json"))) !.Where(library => library.Tags.Contains("lcu") || library.Tags.Contains("ingame")) @@ -22,8 +48,27 @@ public partial class HomeViewModel : PageBase [ObservableProperty] private Vector _librariesOffset = new(); - public override Task InitializeAsync() + [ObservableProperty] + private List _hextechDocsPosts = []; + + [ObservableProperty] + private int _selectedHextechDocsPostsIndex; + + public override async Task InitializeAsync() { - return Task.CompletedTask; + try + { + var posts = await _hextechDocsPostService.GetPostsAsync(); + var hextechDocsPosts = posts.Select(post => new HextechDocsPostViewModel(post)).ToList(); + Dispatcher.UIThread.Invoke(() => + { + HextechDocsPosts = hextechDocsPosts; + }); + } + catch (Exception ex) + { + this.Log() + .Error(ex, "Failed to get posts from HextechDocs."); + } } } diff --git a/Needlework.Net/Views/Pages/Home/HextechDocsPostView.axaml b/Needlework.Net/Views/Pages/Home/HextechDocsPostView.axaml new file mode 100644 index 0000000..e188d0b --- /dev/null +++ b/Needlework.Net/Views/Pages/Home/HextechDocsPostView.axaml @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + diff --git a/Needlework.Net/Views/Pages/Home/HextechDocsPostView.axaml.cs b/Needlework.Net/Views/Pages/Home/HextechDocsPostView.axaml.cs new file mode 100644 index 0000000..ad07aef --- /dev/null +++ b/Needlework.Net/Views/Pages/Home/HextechDocsPostView.axaml.cs @@ -0,0 +1,11 @@ +using Avalonia.Controls; + +namespace Needlework.Net.Views.Pages.Home; + +public partial class HextechDocsPostView : UserControl +{ + public HextechDocsPostView() + { + InitializeComponent(); + } +} \ No newline at end of file diff --git a/Needlework.Net/Views/Pages/Home/HomeView.axaml b/Needlework.Net/Views/Pages/Home/HomeView.axaml index eac1300..e8aca1d 100644 --- a/Needlework.Net/Views/Pages/Home/HomeView.axaml +++ b/Needlework.Net/Views/Pages/Home/HomeView.axaml @@ -40,6 +40,20 @@ Get started with LCU or Game Client development by clicking on the endpoints tab in the left panel. + + + + + + + + + + + THE PROGRAM IS PROVIDED “AS IS” WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES OF MERCHANTABILITY, NONINFRINGMENT, OR OF FITNESS FOR A PARTICULAR PURPOSE. LICENSOR DOES NOT WARRANT THAT THE FUNCTIONS CONTAINED IN THE PROGRAM WILL MEET YOUR REQUIREMENTS OR THAT OPERATION WILL BE UNINTERRUPTED OR ERROR FREE. LICENSOR MAKES NO WARRANTIES RESPECTING ANY HARM THAT MAY BE CAUSED BY MALICIOUS USE OF THIS SOFTWARE. LICENSOR FURTHER EXPRESSLY DISCLAIMS ANY WARRANTY OR REPRESENTATION TO AUTHORIZED USERS OR TO ANY THIRD PARTY.