Added more UI logic. Added a manga pipeline test.
This commit is contained in:
8
MangaReader.Core/Common/Language.cs
Normal file
8
MangaReader.Core/Common/Language.cs
Normal file
@@ -0,0 +1,8 @@
|
||||
namespace MangaReader.Core.Common;
|
||||
|
||||
public enum Language
|
||||
{
|
||||
Japanese,
|
||||
Romanji,
|
||||
English
|
||||
}
|
||||
@@ -81,12 +81,12 @@ public class MangaContext(DbContextOptions options) : DbContext(options)
|
||||
.HasKey(mangaTitle => mangaTitle.MangaTitleId);
|
||||
|
||||
modelBuilder.Entity<MangaTitle>()
|
||||
.HasIndex(mangaTitle => new { mangaTitle.MangaId, mangaTitle.TitleEntry })
|
||||
.HasIndex(mangaTitle => new { mangaTitle.MangaId, mangaTitle.Name })
|
||||
.IsUnique();
|
||||
|
||||
modelBuilder
|
||||
.Entity<MangaTitle>()
|
||||
.HasIndex(mangaTitle => mangaTitle.TitleEntry);
|
||||
.HasIndex(mangaTitle => mangaTitle.Name);
|
||||
|
||||
modelBuilder
|
||||
.Entity<MangaTitle>()
|
||||
|
||||
14
MangaReader.Core/Data/MangaDescription.cs
Normal file
14
MangaReader.Core/Data/MangaDescription.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
using MangaReader.Core.Common;
|
||||
|
||||
namespace MangaReader.Core.Data;
|
||||
|
||||
public class MangaDescription
|
||||
{
|
||||
public int MangaTitleId { get; set; }
|
||||
|
||||
public int MangaId { get; set; }
|
||||
public required Manga Manga { get; set; }
|
||||
|
||||
public required string Name { get; set; }
|
||||
public required Language Language { get; set; }
|
||||
}
|
||||
@@ -7,6 +7,6 @@ public class MangaTitle
|
||||
public int MangaId { get; set; }
|
||||
public required Manga Manga { get; set; }
|
||||
|
||||
public required string TitleEntry { get; set; }
|
||||
public required string Name { get; set; }
|
||||
public TitleType TitleType { get; set; }
|
||||
}
|
||||
@@ -1,8 +1,6 @@
|
||||
using MangaReader.Core.Metadata;
|
||||
|
||||
namespace MangaReader.Core.Pipeline;
|
||||
namespace MangaReader.Core.Pipeline;
|
||||
|
||||
public interface IMangaPipeline
|
||||
{
|
||||
Task RunAsync(SourceManga mangaDto);
|
||||
Task RunAsync(MangaPipelineRequest request);
|
||||
}
|
||||
@@ -7,8 +7,12 @@ namespace MangaReader.Core.Pipeline;
|
||||
|
||||
public partial class MangaPipeline(MangaContext context) : IMangaPipeline
|
||||
{
|
||||
public async Task RunAsync(SourceManga sourceManga)
|
||||
public async Task RunAsync(MangaPipelineRequest request)
|
||||
{
|
||||
string sourceName = request.SourceName;
|
||||
SourceManga sourceManga = request.SourceManga;
|
||||
|
||||
Source source = await GetOrAddSourceAsync(sourceName);
|
||||
Manga manga = await GetOrAddMangaAsync(sourceManga);
|
||||
|
||||
foreach (SourceMangaTitle alternateTitle in sourceManga.AlternateTitles)
|
||||
@@ -29,6 +33,23 @@ public partial class MangaPipeline(MangaContext context) : IMangaPipeline
|
||||
context.SaveChanges();
|
||||
}
|
||||
|
||||
private async Task<Source> GetOrAddSourceAsync(string sourceName)
|
||||
{
|
||||
Source? source = await context.Sources.FirstOrDefaultAsync(s => s.Name == sourceName);
|
||||
|
||||
if (source != null)
|
||||
return source;
|
||||
|
||||
source = new()
|
||||
{
|
||||
Name = sourceName
|
||||
};
|
||||
|
||||
context.Sources.Add(source);
|
||||
|
||||
return source;
|
||||
}
|
||||
|
||||
private async Task<Manga> GetOrAddMangaAsync(SourceManga sourceManga)
|
||||
{
|
||||
Manga? manga = await context.Mangas.FirstOrDefaultAsync(manga => manga.Title == sourceManga.Title);
|
||||
@@ -65,7 +86,7 @@ public partial class MangaPipeline(MangaContext context) : IMangaPipeline
|
||||
private async Task AddTitleAsync(Manga manga, SourceMangaTitle sourceMangaTitle)
|
||||
{
|
||||
MangaTitle? mangaTitle = await context.MangaTitles.FirstOrDefaultAsync(mt =>
|
||||
mt.Manga == manga && mt.TitleEntry == sourceMangaTitle.Title);
|
||||
mt.Manga == manga && mt.Name == sourceMangaTitle.Title);
|
||||
|
||||
if (mangaTitle != null)
|
||||
return;
|
||||
@@ -73,7 +94,7 @@ public partial class MangaPipeline(MangaContext context) : IMangaPipeline
|
||||
mangaTitle = new()
|
||||
{
|
||||
Manga = manga,
|
||||
TitleEntry = sourceMangaTitle.Title,
|
||||
Name = sourceMangaTitle.Title,
|
||||
};
|
||||
|
||||
context.MangaTitles.Add(mangaTitle);
|
||||
|
||||
9
MangaReader.Core/Pipeline/MangaPipelineRequest.cs
Normal file
9
MangaReader.Core/Pipeline/MangaPipelineRequest.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
using MangaReader.Core.Metadata;
|
||||
|
||||
namespace MangaReader.Core.Pipeline;
|
||||
|
||||
public class MangaPipelineRequest
|
||||
{
|
||||
public required string SourceName { get; init; }
|
||||
public required SourceManga SourceManga { get; init; }
|
||||
}
|
||||
@@ -7,4 +7,5 @@ public record MangaSearchResult
|
||||
public string? Thumbnail { get; init; }
|
||||
public string? Author { get; init; }
|
||||
public string? Description { get; init; }
|
||||
public string[] Genres { get; init; } = [];
|
||||
}
|
||||
@@ -67,6 +67,7 @@ public partial class MangaDexSearchProvider(IMangaDexClient mangaDexClient) : IM
|
||||
{
|
||||
Title = title,
|
||||
Description = GetDescription(mangaAttributes),
|
||||
Genres = GetGenres(mangaAttributes),
|
||||
Url = $"https://mangadex.org/title/{mangaEntity.Id}/{slug}",
|
||||
Thumbnail = GetThumbnail(mangaEntity, coverArtEntites)
|
||||
};
|
||||
@@ -95,9 +96,33 @@ public partial class MangaDexSearchProvider(IMangaDexClient mangaDexClient) : IM
|
||||
if (attributes.Description.TryGetValue("en", out string? description))
|
||||
return description;
|
||||
|
||||
if (attributes.Description.Count > 0)
|
||||
return attributes.Description.ElementAt(0).Value;
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
private static string[] GetGenres(MangaAttributes attributes)
|
||||
{
|
||||
if (attributes.Tags == null || attributes.Tags.Count == 0)
|
||||
return [];
|
||||
|
||||
List<string> tags = [];
|
||||
|
||||
foreach (TagEntity tagEntity in attributes.Tags)
|
||||
{
|
||||
if (tagEntity.Attributes == null)
|
||||
continue;
|
||||
|
||||
if (tagEntity.Attributes.Name == null || tagEntity.Attributes.Name.Count == 0)
|
||||
continue;
|
||||
|
||||
tags.Add(tagEntity.Attributes.Name.FirstOrDefault().Value);
|
||||
}
|
||||
|
||||
return [.. tags];
|
||||
}
|
||||
|
||||
public static string GenerateSlug(string title)
|
||||
{
|
||||
// title.ToLowerInvariant().Normalize(NormalizationForm.FormD);
|
||||
|
||||
@@ -40,7 +40,8 @@
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.0" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="9.0.5" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />
|
||||
<PackageReference Include="NSubstitute" Version="5.3.0" />
|
||||
<PackageReference Include="Shouldly" Version="4.3.0" />
|
||||
<PackageReference Include="xunit" Version="2.9.3" />
|
||||
|
||||
53
MangaReader.Tests/Pipeline/MangaPipelineTests.cs
Normal file
53
MangaReader.Tests/Pipeline/MangaPipelineTests.cs
Normal file
@@ -0,0 +1,53 @@
|
||||
using MangaReader.Core.Data;
|
||||
using MangaReader.Core.Metadata;
|
||||
using MangaReader.Core.Pipeline;
|
||||
using MangaReader.Tests.Utilities;
|
||||
|
||||
namespace MangaReader.Tests.Pipeline;
|
||||
|
||||
public class MangaPipelineTests(TestDbContextFactory factory) : IClassFixture<TestDbContextFactory>
|
||||
{
|
||||
[Fact]
|
||||
public async Task RunAsync_SavesMangaTitlesChaptersGenres()
|
||||
{
|
||||
using MangaContext context = factory.CreateContext();
|
||||
var pipeline = new MangaPipeline(context);
|
||||
|
||||
var sourceManga = new SourceManga
|
||||
{
|
||||
Title = "Fullmetal Alchemist",
|
||||
AlternateTitles =
|
||||
[
|
||||
new()
|
||||
{
|
||||
Title = "Hagane no Renkinjutsushi",
|
||||
Language = SourceMangaLanguage.Romanji
|
||||
}
|
||||
],
|
||||
Genres = ["Action", "Adventure"],
|
||||
Chapters =
|
||||
[
|
||||
new()
|
||||
{
|
||||
Number = 1,
|
||||
Title = "The Two Alchemists",
|
||||
Volume = 1,
|
||||
Url = string.Empty
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
MangaPipelineRequest request = new()
|
||||
{
|
||||
SourceName = "MySource",
|
||||
SourceManga = sourceManga
|
||||
};
|
||||
|
||||
await pipeline.RunAsync(request);
|
||||
|
||||
Assert.Single(context.Mangas);
|
||||
Assert.Single(context.MangaTitles);
|
||||
Assert.Equal(2, context.Genres.Count());
|
||||
Assert.Single(context.MangaChapters);
|
||||
}
|
||||
}
|
||||
36
MangaReader.Tests/Utilities/TestDbContextFactory.cs
Normal file
36
MangaReader.Tests/Utilities/TestDbContextFactory.cs
Normal file
@@ -0,0 +1,36 @@
|
||||
using MangaReader.Core.Data;
|
||||
using Microsoft.Data.Sqlite;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace MangaReader.Tests.Utilities;
|
||||
|
||||
public class TestDbContextFactory : IDisposable
|
||||
{
|
||||
private readonly SqliteConnection _connection;
|
||||
private readonly DbContextOptions<MangaContext> _options;
|
||||
|
||||
public TestDbContextFactory()
|
||||
{
|
||||
_connection = new SqliteConnection("DataSource=:memory:");
|
||||
_connection.Open();
|
||||
|
||||
_options = new DbContextOptionsBuilder<MangaContext>()
|
||||
.UseSqlite(_connection)
|
||||
.EnableSensitiveDataLogging() // Optional: helps with debugging
|
||||
.Options;
|
||||
|
||||
using MangaContext context = new(_options);
|
||||
context.Database.EnsureCreated();
|
||||
}
|
||||
|
||||
public MangaContext CreateContext()
|
||||
{
|
||||
return new MangaContext(_options);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_connection.Close();
|
||||
_connection.Dispose();
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,7 @@
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<XamlControlsResources xmlns="using:Microsoft.UI.Xaml.Controls" />
|
||||
<!-- Other merged dictionaries here -->
|
||||
<ResourceDictionary Source="/Resources/Converters.xaml"/>
|
||||
<ResourceDictionary Source="/Resources/Fonts.xaml"/>
|
||||
<ResourceDictionary Source="/Resources/Styles.xaml"/>
|
||||
<ResourceDictionary Source="/Resources/ViewModels.xaml"/>
|
||||
|
||||
BIN
MangaReader.WinUI/Assets/Images/MangaReader.png
Normal file
BIN
MangaReader.WinUI/Assets/Images/MangaReader.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.2 MiB |
20
MangaReader.WinUI/Converters/UppercaseConverter.cs
Normal file
20
MangaReader.WinUI/Converters/UppercaseConverter.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
using Microsoft.UI.Xaml.Data;
|
||||
using System;
|
||||
|
||||
namespace MangaReader.WinUI.Converters;
|
||||
|
||||
public partial class UppercaseConverter : IValueConverter
|
||||
{
|
||||
public object? Convert(object value, Type targetType, object parameter, string language)
|
||||
{
|
||||
if (value == null)
|
||||
return null;
|
||||
|
||||
return value.ToString().ToUpperInvariant();
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, string language)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
@@ -16,9 +16,18 @@
|
||||
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"></RowDefinition>
|
||||
<RowDefinition Height="*"></RowDefinition>
|
||||
</Grid.RowDefinitions>
|
||||
<views:SearchView></views:SearchView>
|
||||
<Grid x:Name="AppTitleBar" Grid.Row="0" VerticalAlignment="Center" Padding="10">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"></ColumnDefinition>
|
||||
<ColumnDefinition Width="*"></ColumnDefinition>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Image Grid.Column="0" x:Name="TitleBarIcon" Source="ms-appx:///Assets/Images/MangaReader.png" Width="20" Height="20" Margin="0 0 10 0" />
|
||||
<TextBlock Grid.Column="1" x:Name="AppTitle" Text="{x:Bind Title, Mode=OneWay}" Style="{StaticResource CaptionTextBlockStyle}" VerticalAlignment="Center" />
|
||||
</Grid>
|
||||
<views:SearchView Grid.Row="1"></views:SearchView>
|
||||
<!--<StackPanel Orientation="Vertical">
|
||||
<StackPanel Orientation="Horizontal" Spacing="10">
|
||||
<TextBox Name="KeywordTextBox" Width="300"></TextBox>
|
||||
|
||||
@@ -1,62 +1,19 @@
|
||||
using MangaReader.Core.Search;
|
||||
using MangaReader.Core.Sources.MangaDex.Api;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Microsoft.UI.Xaml.Controls.Primitives;
|
||||
using Microsoft.UI.Xaml.Data;
|
||||
using Microsoft.UI.Xaml.Input;
|
||||
using Microsoft.UI.Xaml.Media;
|
||||
using Microsoft.UI.Xaml.Navigation;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices.WindowsRuntime;
|
||||
using System.Threading;
|
||||
using Windows.Foundation;
|
||||
using Windows.Foundation.Collections;
|
||||
|
||||
// To learn more about WinUI, the WinUI project structure,
|
||||
// and more about our project templates, see: http://aka.ms/winui-project-info.
|
||||
namespace MangaReader.WinUI;
|
||||
|
||||
namespace MangaReader.WinUI
|
||||
public sealed partial class MainWindow : Window
|
||||
{
|
||||
/// <summary>
|
||||
/// An empty window that can be used on its own or navigated to within a Frame.
|
||||
/// </summary>
|
||||
public sealed partial class MainWindow : Window
|
||||
private const string ApplicationTitle = "Manga Reader";
|
||||
|
||||
public MainWindow(IMangaSearchCoordinator mangaSearchCoordinator, IMangaDexClient mangaDexClient)
|
||||
{
|
||||
private readonly IMangaSearchCoordinator _mangaSearchCoordinator;
|
||||
private readonly IMangaDexClient _mangaDexClient;
|
||||
InitializeComponent();
|
||||
|
||||
private CancellationTokenSource? _cancellationTokenSource;
|
||||
|
||||
public MainWindow(IMangaSearchCoordinator mangaSearchCoordinator, IMangaDexClient mangaDexClient)
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
_mangaSearchCoordinator = mangaSearchCoordinator;
|
||||
_mangaDexClient = mangaDexClient;
|
||||
}
|
||||
|
||||
//private async void Button_Click(object sender, RoutedEventArgs e)
|
||||
//{
|
||||
// if (string.IsNullOrWhiteSpace(KeywordTextBox.Text))
|
||||
// return;
|
||||
|
||||
// _cancellationTokenSource?.Cancel();
|
||||
// _cancellationTokenSource = new();
|
||||
|
||||
// var result = await _mangaSearchCoordinator.SearchAsync(KeywordTextBox.Text, _cancellationTokenSource.Token);
|
||||
|
||||
// //Guid mangaGuid = new("a920060c-7e39-4ac1-980c-f0e605a40ae4");
|
||||
// //var coverArtResult = await _mangaDexClient.GetCoverArtAsync(mangaGuid, _cancellationTokenSource.Token);
|
||||
|
||||
// // if ( (coverArtResult is MangaDexC)
|
||||
// // {
|
||||
|
||||
// // }
|
||||
// // if (coverArtResult.)
|
||||
//}
|
||||
Title = ApplicationTitle;
|
||||
ExtendsContentIntoTitleBar = true; // enable custom titlebar
|
||||
SetTitleBar(AppTitleBar); // set user ui element as titlebar
|
||||
}
|
||||
}
|
||||
@@ -18,6 +18,7 @@
|
||||
<None Remove="Assets\Fonts\Poppins-Medium.otf" />
|
||||
<None Remove="Assets\Fonts\Poppins-Regular.otf" />
|
||||
<None Remove="Assets\Fonts\Poppins-SemiBold.otf" />
|
||||
<None Remove="Assets\Images\MangaReader.png" />
|
||||
<None Remove="Resources\ViewModels.xaml" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -29,6 +30,7 @@
|
||||
<Content Include="Assets\Square44x44Logo.targetsize-24_altform-unplated.png" />
|
||||
<Content Include="Assets\StoreLogo.png" />
|
||||
<Content Include="Assets\Wide310x150Logo.scale-200.png" />
|
||||
<Content Include="MangaReader.ico" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@@ -45,6 +47,7 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.0" />
|
||||
<PackageReference Include="CommunityToolkit.WinUI.Controls.Primitives" Version="8.2.250402" />
|
||||
<PackageReference Include="CommunityToolkit.WinUI.Media" Version="8.2.250402" />
|
||||
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.26100.4188" />
|
||||
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.7.250513003" />
|
||||
@@ -53,6 +56,9 @@
|
||||
<ProjectReference Include="..\MangaReader.Core\MangaReader.Core.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Page Update="Resources\Converters.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
<Page Update="Resources\Styles.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
@@ -84,5 +90,6 @@
|
||||
<PublishReadyToRun Condition="'$(Configuration)' != 'Debug'">True</PublishReadyToRun>
|
||||
<PublishTrimmed Condition="'$(Configuration)' == 'Debug'">False</PublishTrimmed>
|
||||
<PublishTrimmed Condition="'$(Configuration)' != 'Debug'">True</PublishTrimmed>
|
||||
<ApplicationIcon>MangaReader.ico</ApplicationIcon>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
BIN
MangaReader.WinUI/MangaReader.ico
Normal file
BIN
MangaReader.WinUI/MangaReader.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 206 KiB |
9
MangaReader.WinUI/Resources/Converters.xaml
Normal file
9
MangaReader.WinUI/Resources/Converters.xaml
Normal file
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ResourceDictionary
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:converters="using:MangaReader.WinUI.Converters">
|
||||
|
||||
<converters:UppercaseConverter x:Key="UppercaseConverter" />
|
||||
|
||||
</ResourceDictionary>
|
||||
@@ -7,47 +7,69 @@
|
||||
xmlns:vm="using:MangaReader.WinUI.ViewModels"
|
||||
xmlns:search="using:MangaReader.Core.Search"
|
||||
xmlns:UI="using:CommunityToolkit.WinUI"
|
||||
xmlns:Controls="using:CommunityToolkit.WinUI.Controls"
|
||||
xmlns:Media="using:CommunityToolkit.WinUI.Media"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
DataContext="{Binding Source={StaticResource Locator}, Path=SearchViewModel}"
|
||||
d:DataContext="{d:DesignInstance Type=vm:SearchViewModel, IsDesignTimeCreatable=True}"
|
||||
mc:Ignorable="d">
|
||||
<UserControl.Resources>
|
||||
<Media:AttachedCardShadow x:Key="CommonShadow" Offset="5" BlurRadius="10" Opacity=".4" />
|
||||
|
||||
<DataTemplate x:Key="MangaSearchResultTemplate" x:DataType="search:MangaSearchResult">
|
||||
<Grid Padding="20" ColumnSpacing="20" MaxHeight="600" VerticalAlignment="Top" Background="{StaticResource CardBackgroundFillColorDefault}" CornerRadius="8">
|
||||
<Grid Padding="20" ColumnSpacing="20" MaxHeight="400" VerticalAlignment="Stretch" Background="{StaticResource CardBackgroundFillColorDefault}" CornerRadius="8">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"></ColumnDefinition>
|
||||
<ColumnDefinition Width="*"></ColumnDefinition>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Border Grid.Column="0" Width="300">
|
||||
<Border Grid.Column="0" MaxWidth="300" UI:Effects.Shadow="{StaticResource CommonShadow}" VerticalAlignment="Top" HorizontalAlignment="Left">
|
||||
<Grid VerticalAlignment="Top" HorizontalAlignment="Left" CornerRadius="8">
|
||||
<Image Source="{x:Bind Thumbnail, Mode=OneWay}" MaxWidth="300" UI:Effects.Shadow="{StaticResource CommonShadow}"></Image>
|
||||
<Image Source="{x:Bind Thumbnail, Mode=OneWay}" MaxWidth="300"></Image>
|
||||
<Canvas Background="#19000000"></Canvas>
|
||||
</Grid>
|
||||
</Border>
|
||||
<Grid Grid.Column="1" RowSpacing="20">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"></RowDefinition>
|
||||
<RowDefinition Height="Auto"></RowDefinition>
|
||||
<RowDefinition Height="*"></RowDefinition>
|
||||
</Grid.RowDefinitions>
|
||||
<TextBlock Grid.Row="0" Text="{x:Bind Title}" FontSize="24" FontFamily="{StaticResource PoppinsSemiBold}" TextWrapping="Wrap"></TextBlock>
|
||||
<ScrollViewer Grid.Row="1">
|
||||
<ItemsControl Grid.Row="1" ItemsSource="{x:Bind Genres, Mode=OneWay}" ItemTemplate="{StaticResource GenreTemplate}">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<Controls:WrapPanel Orientation="Horizontal" HorizontalSpacing="10" VerticalSpacing="10" />
|
||||
</ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
</ItemsControl>
|
||||
<ScrollViewer Grid.Row="3">
|
||||
<TextBlock Text="{x:Bind Description}" Foreground="{StaticResource TextFillColorSecondaryBrush}" FontSize="16" TextWrapping="Wrap" LineStackingStrategy="BlockLineHeight" LineHeight="22"></TextBlock>
|
||||
</ScrollViewer>
|
||||
|
||||
</Grid>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
|
||||
<DataTemplate x:Key="GenreTemplate" x:DataType="x:String">
|
||||
<Border>
|
||||
<TextBlock FontSize="12" Foreground="{StaticResource TextFillColorTertiary}" Text="{x:Bind Mode=OneWay, Converter={StaticResource UppercaseConverter}}" FontFamily="{StaticResource PoppinsSemiBold}"></TextBlock>
|
||||
</Border>
|
||||
</DataTemplate>
|
||||
|
||||
</UserControl.Resources>
|
||||
<Grid Padding="0" RowSpacing="20">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"></RowDefinition>
|
||||
<RowDefinition Height="*"></RowDefinition>
|
||||
</Grid.RowDefinitions>
|
||||
<StackPanel Orientation="Horizontal" Spacing="10" HorizontalAlignment="Center" Padding="20">
|
||||
<TextBox Text="{Binding Keyword, Mode=TwoWay}" Width="300"></TextBox>
|
||||
<Button Content="Search" Command="{Binding SearchCommand}"></Button>
|
||||
</StackPanel>
|
||||
<Border HorizontalAlignment="Stretch">
|
||||
<StackPanel Orientation="Horizontal" Spacing="10" HorizontalAlignment="Center" Padding="10">
|
||||
<TextBox Text="{Binding Keyword, Mode=TwoWay}" Width="300"></TextBox>
|
||||
<Button Content="Search" Command="{Binding SearchCommand}"></Button>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<ScrollViewer Grid.Row="1" RenderTransformOrigin=".5,.5" Padding="50">
|
||||
<ScrollViewer.RenderTransform>
|
||||
<ScaleTransform ScaleX="1" ScaleY="1" />
|
||||
|
||||
Reference in New Issue
Block a user