mirror of
https://github.com/BlossomiShymae/Needlework.Net.git
synced 2025-12-06 02:00:47 +01:00
feat: add HextechDocs posts carousel
This commit is contained in:
BIN
Needlework.Net/Assets/bg-event-pass.jpg
Normal file
BIN
Needlework.Net/Assets/bg-event-pass.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 79 KiB |
13
Needlework.Net/Models/HextechDocsPost.cs
Normal file
13
Needlework.Net/Models/HextechDocsPost.cs
Normal file
@@ -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}";
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="AngleSharp" Version="1.3.0" />
|
||||
<PackageReference Include="Avalonia" Version="11.2.8" />
|
||||
<PackageReference Include="Avalonia.AvaloniaEdit" Version="11.3.0" />
|
||||
<PackageReference Include="Avalonia.Controls.DataGrid" Version="11.2.8" />
|
||||
|
||||
@@ -87,6 +87,7 @@ class Program
|
||||
// HOME
|
||||
locator.Register<HomeViewModel>(() => new HomeView());
|
||||
locator.Register<LibraryViewModel>(() => new LibraryView());
|
||||
locator.Register<HextechDocsPostViewModel>(() => new HextechDocsPostView());
|
||||
// SCHEMAS
|
||||
locator.Register<SchemasViewModel>(() => new SchemasView());
|
||||
locator.Register<ViewModels.Pages.Schemas.SchemaSearchDetailsViewModel>(() => new Views.Pages.Schemas.SchemaSearchDetailsView());
|
||||
@@ -103,6 +104,7 @@ class Program
|
||||
builder.AddSingleton<DocumentService>();
|
||||
builder.AddSingleton<NotificationService>();
|
||||
builder.AddSingleton<SchemaPaneService>();
|
||||
builder.AddSingleton<HextechDocsPostService>();
|
||||
builder.AddSingleton<IFlurlClientCache>(new FlurlClientCache()
|
||||
.Add("GithubClient", "https://api.github.com")
|
||||
.Add("GithubUserContentClient", "https://raw.githubusercontent.com")
|
||||
|
||||
41
Needlework.Net/Services/HextechDocsPostService.cs
Normal file
41
Needlework.Net/Services/HextechDocsPostService.cs
Normal file
@@ -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<List<HextechDocsPost>> GetPostsAsync()
|
||||
{
|
||||
if (Cached<List<HextechDocsPost>>.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<HextechDocsPost>();
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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; }
|
||||
}
|
||||
}
|
||||
@@ -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<LibraryViewModel> Libraries { get; } = JsonSerializer.Deserialize<List<Library>>(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<HextechDocsPostViewModel> _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.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
37
Needlework.Net/Views/Pages/Home/HextechDocsPostView.axaml
Normal file
37
Needlework.Net/Views/Pages/Home/HextechDocsPostView.axaml
Normal file
@@ -0,0 +1,37 @@
|
||||
<UserControl xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:vm="using:Needlework.Net.ViewModels.Pages.Home"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||
x:Class="Needlework.Net.Views.Pages.Home.HextechDocsPostView"
|
||||
x:DataType="vm:HextechDocsPostViewModel">
|
||||
<Border BorderBrush="{DynamicResource ControlStrokeColorOnAccentTertiaryBrush}"
|
||||
BorderThickness="1"
|
||||
CornerRadius="4"
|
||||
Height="80">
|
||||
<Grid>
|
||||
<Border CornerRadius="4"
|
||||
ClipToBounds="True">
|
||||
<Image Source="/Assets/bg-event-pass.jpg"
|
||||
Stretch="UniformToFill"/>
|
||||
</Border>
|
||||
<Border Padding="8">
|
||||
<Grid RowDefinitions="*,*,*">
|
||||
<TextBlock Text="{Binding HextechDocsPost.Title}"
|
||||
Theme="{StaticResource BodyStrongTextBlockStyle}"
|
||||
TextTrimming="CharacterEllipsis"
|
||||
Grid.Row="0"/>
|
||||
<TextBlock Text="{Binding HextechDocsPost.Excerpt}"
|
||||
TextTrimming="CharacterEllipsis"
|
||||
Grid.Row="1"/>
|
||||
<HyperlinkButton Padding="0"
|
||||
Content="{Binding HextechDocsPost.Url}"
|
||||
NavigateUri="{Binding HextechDocsPost.Url}"
|
||||
Grid.Row="2"/>
|
||||
</Grid>
|
||||
</Border>
|
||||
</Grid>
|
||||
</Border>
|
||||
|
||||
</UserControl>
|
||||
11
Needlework.Net/Views/Pages/Home/HextechDocsPostView.axaml.cs
Normal file
11
Needlework.Net/Views/Pages/Home/HextechDocsPostView.axaml.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
using Avalonia.Controls;
|
||||
|
||||
namespace Needlework.Net.Views.Pages.Home;
|
||||
|
||||
public partial class HextechDocsPostView : UserControl
|
||||
{
|
||||
public HextechDocsPostView()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
@@ -40,6 +40,20 @@
|
||||
<TextBlock>Get started with LCU or Game Client development by clicking on the endpoints tab in the left panel.</TextBlock>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
<!-- HEXTECHDOCS POSTS -->
|
||||
<Carousel ItemsSource="{Binding HextechDocsPosts}"
|
||||
SelectedIndex="{Binding SelectedHextechDocsPostsIndex}"
|
||||
Margin="12"
|
||||
IsVisible="{Binding SelectedHextechDocsPostsIndex, Converter={StaticResource EnumerableToVisibilityConverter}}">
|
||||
<Carousel.PageTransition>
|
||||
<PageSlide Orientation="Horizontal" Duration="0.25"/>
|
||||
</Carousel.PageTransition>
|
||||
<Carousel.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<ContentControl Content="{Binding}"/>
|
||||
</DataTemplate>
|
||||
</Carousel.ItemTemplate>
|
||||
</Carousel>
|
||||
<controls:Card Margin="12">
|
||||
<TextBlock TextWrapping="Wrap">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.</TextBlock>
|
||||
</controls:Card>
|
||||
|
||||
Reference in New Issue
Block a user