More updates.
This commit is contained in:
8
MangaReader.Core/Common/ContributorRole.cs
Normal file
8
MangaReader.Core/Common/ContributorRole.cs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
namespace MangaReader.Core.Common;
|
||||||
|
|
||||||
|
public enum ContributorRole
|
||||||
|
{
|
||||||
|
Unknown,
|
||||||
|
Author,
|
||||||
|
Artist
|
||||||
|
}
|
||||||
@@ -7,7 +7,7 @@ public class Manga
|
|||||||
|
|
||||||
public virtual ICollection<MangaCover> Covers { get; set; } = [];
|
public virtual ICollection<MangaCover> Covers { get; set; } = [];
|
||||||
public virtual ICollection<MangaTitle> Titles { get; set; } = [];
|
public virtual ICollection<MangaTitle> Titles { get; set; } = [];
|
||||||
public virtual ICollection<MangaDescription> Descriptions { get; set; } = [];
|
//public virtual ICollection<MangaDescription> Descriptions { get; set; } = [];
|
||||||
public virtual ICollection<MangaSource> Sources { get; set; } = [];
|
public virtual ICollection<MangaSource> Sources { get; set; } = [];
|
||||||
public virtual ICollection<MangaContributor> Contributors { get; set; } = [];
|
public virtual ICollection<MangaContributor> Contributors { get; set; } = [];
|
||||||
public virtual ICollection<MangaGenre> Genres { get; set; } = [];
|
public virtual ICollection<MangaGenre> Genres { get; set; } = [];
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ public class MangaContext(DbContextOptions options) : DbContext(options)
|
|||||||
//ConfigureMangaChapter(modelBuilder);
|
//ConfigureMangaChapter(modelBuilder);
|
||||||
//ConfigureChapterSource(modelBuilder);
|
//ConfigureChapterSource(modelBuilder);
|
||||||
//ConfigureChapterPage(modelBuilder);
|
//ConfigureChapterPage(modelBuilder);
|
||||||
ConfigureSourceChapter(modelBuilder);
|
ConfigureMangaSourceChapter(modelBuilder);
|
||||||
ConfigureSourcePage(modelBuilder);
|
ConfigureSourcePage(modelBuilder);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -114,29 +114,29 @@ public class MangaContext(DbContextOptions options) : DbContext(options)
|
|||||||
{
|
{
|
||||||
modelBuilder
|
modelBuilder
|
||||||
.Entity<MangaDescription>()
|
.Entity<MangaDescription>()
|
||||||
.HasKey(mangaTitle => mangaTitle.MangaTitleId);
|
.HasKey(mangaDescription => mangaDescription.MangaDescriptionId);
|
||||||
|
|
||||||
modelBuilder.Entity<MangaDescription>()
|
modelBuilder.Entity<MangaDescription>()
|
||||||
.Property(mt => mt.Name)
|
.Property(mangaDescription => mangaDescription.Name)
|
||||||
.IsRequired();
|
.IsRequired();
|
||||||
|
|
||||||
modelBuilder.Entity<MangaDescription>()
|
modelBuilder.Entity<MangaDescription>()
|
||||||
.Property(mt => mt.Language)
|
.Property(mangaDescription => mangaDescription.Language)
|
||||||
.IsRequired();
|
.IsRequired();
|
||||||
|
|
||||||
modelBuilder.Entity<MangaDescription>()
|
modelBuilder.Entity<MangaDescription>()
|
||||||
.HasIndex(mangaTitle => new { mangaTitle.MangaId, mangaTitle.Name, mangaTitle.Language })
|
.HasIndex(mangaDescription => new { mangaDescription.MangaSourceId, mangaDescription.Name, mangaDescription.Language })
|
||||||
.IsUnique();
|
.IsUnique();
|
||||||
|
|
||||||
modelBuilder
|
modelBuilder
|
||||||
.Entity<MangaDescription>()
|
.Entity<MangaDescription>()
|
||||||
.HasIndex(mangaTitle => mangaTitle.Name);
|
.HasIndex(mangaDescription => mangaDescription.Name);
|
||||||
|
|
||||||
modelBuilder
|
modelBuilder
|
||||||
.Entity<MangaDescription>()
|
.Entity<MangaDescription>()
|
||||||
.HasOne(x => x.Manga)
|
.HasOne(x => x.MangaSource)
|
||||||
.WithMany(x => x.Descriptions)
|
.WithMany(x => x.Descriptions)
|
||||||
.HasForeignKey(x => x.MangaId)
|
.HasForeignKey(x => x.MangaSourceId)
|
||||||
.OnDelete(DeleteBehavior.Cascade);
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -273,7 +273,7 @@ public class MangaContext(DbContextOptions options) : DbContext(options)
|
|||||||
// .OnDelete(DeleteBehavior.Cascade);
|
// .OnDelete(DeleteBehavior.Cascade);
|
||||||
//}
|
//}
|
||||||
|
|
||||||
private static void ConfigureSourceChapter(ModelBuilder modelBuilder)
|
private static void ConfigureMangaSourceChapter(ModelBuilder modelBuilder)
|
||||||
{
|
{
|
||||||
modelBuilder
|
modelBuilder
|
||||||
.Entity<SourceChapter>()
|
.Entity<SourceChapter>()
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
namespace MangaReader.Core.Data;
|
using MangaReader.Core.Common;
|
||||||
|
|
||||||
|
namespace MangaReader.Core.Data;
|
||||||
|
|
||||||
public class MangaContributor
|
public class MangaContributor
|
||||||
{
|
{
|
||||||
@@ -8,5 +10,5 @@ public class MangaContributor
|
|||||||
public int ContributorId { get; set; }
|
public int ContributorId { get; set; }
|
||||||
public required Contributor Contributor { get; set; }
|
public required Contributor Contributor { get; set; }
|
||||||
|
|
||||||
public MangaContributorRole Role { get; set; }
|
public ContributorRole Role { get; set; }
|
||||||
}
|
}
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
namespace MangaReader.Core.Data;
|
|
||||||
|
|
||||||
public enum MangaContributorRole
|
|
||||||
{
|
|
||||||
Author,
|
|
||||||
Artist
|
|
||||||
}
|
|
||||||
@@ -4,10 +4,10 @@ namespace MangaReader.Core.Data;
|
|||||||
|
|
||||||
public class MangaDescription
|
public class MangaDescription
|
||||||
{
|
{
|
||||||
public int MangaTitleId { get; set; }
|
public int MangaDescriptionId { get; set; }
|
||||||
|
|
||||||
public int MangaId { get; set; }
|
public int MangaSourceId { get; set; }
|
||||||
public required Manga Manga { get; set; }
|
public required MangaSource MangaSource { get; set; }
|
||||||
|
|
||||||
public required string Name { get; set; }
|
public required string Name { get; set; }
|
||||||
public required Language Language { get; set; }
|
public required Language Language { get; set; }
|
||||||
|
|||||||
@@ -12,5 +12,6 @@ public class MangaSource
|
|||||||
|
|
||||||
public required string Url { get; set; }
|
public required string Url { get; set; }
|
||||||
|
|
||||||
|
public virtual ICollection<MangaDescription> Descriptions { get; set; } = [];
|
||||||
public virtual ICollection<SourceChapter> Chapters { get; set; } = [];
|
public virtual ICollection<SourceChapter> Chapters { get; set; } = [];
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
using MangaReader.Core.Http;
|
using MangaReader.Core.Data;
|
||||||
|
using MangaReader.Core.Http;
|
||||||
using MangaReader.Core.Metadata;
|
using MangaReader.Core.Metadata;
|
||||||
|
using MangaReader.Core.Pipeline;
|
||||||
using MangaReader.Core.Search;
|
using MangaReader.Core.Search;
|
||||||
using MangaReader.Core.Sources.MangaDex.Api;
|
using MangaReader.Core.Sources.MangaDex.Api;
|
||||||
using MangaReader.Core.Sources.MangaDex.Metadata;
|
using MangaReader.Core.Sources.MangaDex.Metadata;
|
||||||
@@ -7,6 +9,7 @@ using MangaReader.Core.Sources.MangaDex.Search;
|
|||||||
using MangaReader.Core.Sources.NatoManga.Api;
|
using MangaReader.Core.Sources.NatoManga.Api;
|
||||||
using MangaReader.Core.Sources.NatoManga.Metadata;
|
using MangaReader.Core.Sources.NatoManga.Metadata;
|
||||||
using MangaReader.Core.Sources.NatoManga.Search;
|
using MangaReader.Core.Sources.NatoManga.Search;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
#pragma warning disable IDE0130 // Namespace does not match folder structure
|
#pragma warning disable IDE0130 // Namespace does not match folder structure
|
||||||
namespace Microsoft.Extensions.DependencyInjection;
|
namespace Microsoft.Extensions.DependencyInjection;
|
||||||
@@ -14,7 +17,7 @@ namespace Microsoft.Extensions.DependencyInjection;
|
|||||||
|
|
||||||
public static class ServiceCollectionExtensions
|
public static class ServiceCollectionExtensions
|
||||||
{
|
{
|
||||||
public static IServiceCollection AddMangaReader(this IServiceCollection services)
|
public static IServiceCollection AddMangaReader(this IServiceCollection services, Action<DbContextOptionsBuilder>? optionsAction = null)
|
||||||
{
|
{
|
||||||
// Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:139.0) Gecko/20100101 Firefox/139.0
|
// Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:139.0) Gecko/20100101 Firefox/139.0
|
||||||
services.AddHttpClient<IHttpService, HttpService>(client =>
|
services.AddHttpClient<IHttpService, HttpService>(client =>
|
||||||
@@ -34,9 +37,32 @@ public static class ServiceCollectionExtensions
|
|||||||
// MangaDex
|
// MangaDex
|
||||||
services.AddScoped<IMangaDexClient, MangaDexClient>();
|
services.AddScoped<IMangaDexClient, MangaDexClient>();
|
||||||
services.AddScoped<IMangaSearchProvider, MangaDexSearchProvider>();
|
services.AddScoped<IMangaSearchProvider, MangaDexSearchProvider>();
|
||||||
services.AddScoped<IMangaMetadataProvider, MangaDexMetadataProvider>();
|
//services.AddScoped<IMangaMetadataProvider, MangaDexMetadataProvider>();
|
||||||
|
services.AddKeyedScoped<IMangaMetadataProvider, MangaDexMetadataProvider>("MangaDex");
|
||||||
|
|
||||||
services.AddScoped<IMangaSearchCoordinator, MangaSearchCoordinator>();
|
services.AddScoped<IMangaSearchCoordinator, MangaSearchCoordinator>();
|
||||||
|
services.AddScoped<IMangaMetadataCoordinator, MangaMetadataCoordinator>();
|
||||||
|
|
||||||
|
services.AddScoped<IMangaPipeline, MangaPipeline>();
|
||||||
|
|
||||||
|
// Database
|
||||||
|
services.AddDbContext<MangaContext>(options =>
|
||||||
|
{
|
||||||
|
if (optionsAction is not null)
|
||||||
|
{
|
||||||
|
optionsAction(options);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var dbPath = Path.Combine(
|
||||||
|
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
|
||||||
|
"MangaReader",
|
||||||
|
"manga.db");
|
||||||
|
|
||||||
|
Directory.CreateDirectory(Path.GetDirectoryName(dbPath)!);
|
||||||
|
options.UseSqlite($"Data Source={dbPath}");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
return services;
|
return services;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="HtmlAgilityPack" Version="1.12.1" />
|
<PackageReference Include="HtmlAgilityPack" Version="1.12.1" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.6" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.6" />
|
||||||
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="9.0.6" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Http" Version="9.0.6" />
|
<PackageReference Include="Microsoft.Extensions.Http" Version="9.0.6" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|||||||
6
MangaReader.Core/Metadata/IMangaMetadataCoordinator.cs
Normal file
6
MangaReader.Core/Metadata/IMangaMetadataCoordinator.cs
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
namespace MangaReader.Core.Metadata;
|
||||||
|
|
||||||
|
public interface IMangaMetadataCoordinator
|
||||||
|
{
|
||||||
|
IMangaMetadataProvider GetProvider(string sourceName);
|
||||||
|
}
|
||||||
11
MangaReader.Core/Metadata/MangaMetadataCoordinator.cs
Normal file
11
MangaReader.Core/Metadata/MangaMetadataCoordinator.cs
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
||||||
|
namespace MangaReader.Core.Metadata;
|
||||||
|
|
||||||
|
public class MangaMetadataCoordinator(IServiceProvider serviceProvider) : IMangaMetadataCoordinator
|
||||||
|
{
|
||||||
|
public IMangaMetadataProvider GetProvider(string sourceName)
|
||||||
|
{
|
||||||
|
return serviceProvider.GetRequiredKeyedService<IMangaMetadataProvider>(sourceName);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +1,9 @@
|
|||||||
namespace MangaReader.Core.Metadata;
|
using MangaReader.Core.Common;
|
||||||
|
|
||||||
|
namespace MangaReader.Core.Metadata;
|
||||||
|
|
||||||
public class SourceMangaContributor
|
public class SourceMangaContributor
|
||||||
{
|
{
|
||||||
public required string Name { get; set; }
|
public required string Name { get; set; }
|
||||||
public SourceMangaContributorRole Role { get; set; }
|
public ContributorRole Role { get; set; }
|
||||||
}
|
}
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
namespace MangaReader.Core.Metadata;
|
|
||||||
|
|
||||||
public enum SourceMangaContributorRole
|
|
||||||
{
|
|
||||||
Unknown,
|
|
||||||
Author,
|
|
||||||
Artist
|
|
||||||
}
|
|
||||||
@@ -2,6 +2,6 @@
|
|||||||
|
|
||||||
public interface IMangaPipeline
|
public interface IMangaPipeline
|
||||||
{
|
{
|
||||||
Task RunMetadataAsync(MangaMetadataPipelineRequest request);
|
Task RunMetadataAsync(MangaMetadataPipelineRequest request, CancellationToken cancellationToken);
|
||||||
Task RunPagesAsync(MangaPagePipelineRequest request);
|
Task RunPagesAsync(MangaPagePipelineRequest request, CancellationToken cancellationToken);
|
||||||
}
|
}
|
||||||
@@ -13,7 +13,7 @@ public partial class MangaPipeline(MangaContext context) : IMangaPipeline
|
|||||||
Secondary
|
Secondary
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task RunMetadataAsync(MangaMetadataPipelineRequest request)
|
public async Task RunMetadataAsync(MangaMetadataPipelineRequest request, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
string sourceName = request.SourceName;
|
string sourceName = request.SourceName;
|
||||||
string sourceUrl = request.SourceUrl;
|
string sourceUrl = request.SourceUrl;
|
||||||
@@ -24,7 +24,7 @@ public partial class MangaPipeline(MangaContext context) : IMangaPipeline
|
|||||||
MangaSource mangaSource = await AddMangaSourceAsync(sourceUrl, manga, source);
|
MangaSource mangaSource = await AddMangaSourceAsync(sourceUrl, manga, source);
|
||||||
|
|
||||||
await AddTitleAsync(manga, sourceManga.Title, TitleType.Primary);
|
await AddTitleAsync(manga, sourceManga.Title, TitleType.Primary);
|
||||||
await AddDescriptionAsync(manga, sourceManga.Description);
|
await AddDescriptionAsync(mangaSource, sourceManga.Description);
|
||||||
|
|
||||||
foreach (SourceMangaTitle alternateTitle in sourceManga.AlternateTitles)
|
foreach (SourceMangaTitle alternateTitle in sourceManga.AlternateTitles)
|
||||||
{
|
{
|
||||||
@@ -36,6 +36,11 @@ public partial class MangaPipeline(MangaContext context) : IMangaPipeline
|
|||||||
await LinkGenreAsync(manga, genre);
|
await LinkGenreAsync(manga, genre);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
foreach (SourceMangaContributor contributor in sourceManga.Contributors)
|
||||||
|
{
|
||||||
|
await LinkMangaContributorAsync(manga, contributor);
|
||||||
|
}
|
||||||
|
|
||||||
foreach (SourceMangaChapter chapter in sourceManga.Chapters)
|
foreach (SourceMangaChapter chapter in sourceManga.Chapters)
|
||||||
{
|
{
|
||||||
await AddChapterAsync(mangaSource, chapter);
|
await AddChapterAsync(mangaSource, chapter);
|
||||||
@@ -133,20 +138,23 @@ public partial class MangaPipeline(MangaContext context) : IMangaPipeline
|
|||||||
context.MangaTitles.Add(mangaTitle);
|
context.MangaTitles.Add(mangaTitle);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task AddDescriptionAsync(Manga manga, SourceMangaDescription? sourceMangaDescription)
|
private async Task AddDescriptionAsync(MangaSource mangaSource, SourceMangaDescription? sourceMangaDescription)
|
||||||
{
|
{
|
||||||
if (sourceMangaDescription == null)
|
if (sourceMangaDescription == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
MangaDescription? mangaDescription = await context.MangaDescriptions.FirstOrDefaultAsync(md =>
|
MangaDescription? mangaDescription = await context.MangaDescriptions.FirstOrDefaultAsync(md =>
|
||||||
md.Manga == manga && md.Name == sourceMangaDescription.Name);
|
md.MangaSource == mangaSource && md.Language == sourceMangaDescription.Language);
|
||||||
|
|
||||||
if (mangaDescription != null)
|
if (mangaDescription != null)
|
||||||
|
{
|
||||||
|
mangaDescription.Name = sourceMangaDescription.Name;
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
mangaDescription = new()
|
mangaDescription = new()
|
||||||
{
|
{
|
||||||
Manga = manga,
|
MangaSource = mangaSource,
|
||||||
Name = sourceMangaDescription.Name,
|
Name = sourceMangaDescription.Name,
|
||||||
Language = sourceMangaDescription.Language
|
Language = sourceMangaDescription.Language
|
||||||
};
|
};
|
||||||
@@ -189,6 +197,51 @@ public partial class MangaPipeline(MangaContext context) : IMangaPipeline
|
|||||||
return genre;
|
return genre;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task LinkMangaContributorAsync(Manga manga, SourceMangaContributor sourceMangaContributor)
|
||||||
|
{
|
||||||
|
Contributor contributor = await GetOrAddContributorAsync(sourceMangaContributor.Name);
|
||||||
|
|
||||||
|
MangaContributor? mangaContributor = await context.MangaContributors.FirstOrDefaultAsync(x =>
|
||||||
|
x.Manga == manga && x.Contributor == contributor && x.Role == sourceMangaContributor.Role);
|
||||||
|
|
||||||
|
if (mangaContributor != null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
mangaContributor = new()
|
||||||
|
{
|
||||||
|
Manga = manga,
|
||||||
|
Contributor = contributor,
|
||||||
|
Role = sourceMangaContributor.Role
|
||||||
|
};
|
||||||
|
|
||||||
|
context.MangaContributors.Add(mangaContributor);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<Contributor> GetOrAddContributorAsync(string contributorName)
|
||||||
|
{
|
||||||
|
Contributor? trackedContributor = context.ChangeTracker
|
||||||
|
.Entries<Contributor>()
|
||||||
|
.Select(e => e.Entity)
|
||||||
|
.FirstOrDefault(c => c.Name == contributorName);
|
||||||
|
|
||||||
|
if (trackedContributor is not null)
|
||||||
|
return trackedContributor;
|
||||||
|
|
||||||
|
Contributor? contributor = await context.Contributors.FirstOrDefaultAsync(x => x.Name == contributorName);
|
||||||
|
|
||||||
|
if (contributor == null)
|
||||||
|
{
|
||||||
|
contributor = new()
|
||||||
|
{
|
||||||
|
Name = contributorName,
|
||||||
|
};
|
||||||
|
|
||||||
|
await context.Contributors.AddAsync(contributor);
|
||||||
|
}
|
||||||
|
|
||||||
|
return contributor;
|
||||||
|
}
|
||||||
|
|
||||||
private async Task AddChapterAsync(MangaSource mangaSource, SourceMangaChapter sourceMangaChapter)
|
private async Task AddChapterAsync(MangaSource mangaSource, SourceMangaChapter sourceMangaChapter)
|
||||||
{
|
{
|
||||||
SourceChapter sourceChapter = await GetSourceChapter(mangaSource, sourceMangaChapter)
|
SourceChapter sourceChapter = await GetSourceChapter(mangaSource, sourceMangaChapter)
|
||||||
@@ -225,9 +278,9 @@ public partial class MangaPipeline(MangaContext context) : IMangaPipeline
|
|||||||
return sourceChapter;
|
return sourceChapter;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task RunPagesAsync(MangaPagePipelineRequest request)
|
public async Task RunPagesAsync(MangaPagePipelineRequest request, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
SourceChapter? sourceChapter = await context.SourceChapters.FirstOrDefaultAsync(x => x.SourceChapterId == request.SourceChapterId);
|
SourceChapter? sourceChapter = await context.SourceChapters.FirstOrDefaultAsync(x => x.SourceChapterId == request.SourceChapterId, cancellationToken);
|
||||||
|
|
||||||
if (sourceChapter == null)
|
if (sourceChapter == null)
|
||||||
return;
|
return;
|
||||||
@@ -236,14 +289,14 @@ public partial class MangaPipeline(MangaContext context) : IMangaPipeline
|
|||||||
|
|
||||||
foreach (string pageImageUrl in request.PageImageUrls)
|
foreach (string pageImageUrl in request.PageImageUrls)
|
||||||
{
|
{
|
||||||
await AddOrUpdateSourcePageAsync(sourceChapter, currentPageNumber++, pageImageUrl);
|
await AddOrUpdateSourcePageAsync(sourceChapter, currentPageNumber++, pageImageUrl, cancellationToken);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task AddOrUpdateSourcePageAsync(SourceChapter sourceChapter, int pageNumber, string pageImageUrl)
|
private async Task AddOrUpdateSourcePageAsync(SourceChapter sourceChapter, int pageNumber, string pageImageUrl, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
SourcePage? sourcePage = await context.SourcePages.FirstOrDefaultAsync(x =>
|
SourcePage? sourcePage = await context.SourcePages.FirstOrDefaultAsync(x =>
|
||||||
x.Chapter == sourceChapter && x.PageNumber == pageNumber);
|
x.Chapter == sourceChapter && x.PageNumber == pageNumber, cancellationToken);
|
||||||
|
|
||||||
if (sourcePage == null)
|
if (sourcePage == null)
|
||||||
{
|
{
|
||||||
@@ -254,7 +307,7 @@ public partial class MangaPipeline(MangaContext context) : IMangaPipeline
|
|||||||
Url = pageImageUrl
|
Url = pageImageUrl
|
||||||
};
|
};
|
||||||
|
|
||||||
context.SourcePages.Add(sourcePage);
|
await context.SourcePages.AddAsync(sourcePage, cancellationToken);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -137,7 +137,7 @@ public class MangaDexMetadataProvider(IMangaDexClient mangaDexClient) : IMangaMe
|
|||||||
SourceMangaContributor contributor = new()
|
SourceMangaContributor contributor = new()
|
||||||
{
|
{
|
||||||
Name = authorEntity.Attributes.Name,
|
Name = authorEntity.Attributes.Name,
|
||||||
Role = SourceMangaContributorRole.Author
|
Role = ContributorRole.Author
|
||||||
};
|
};
|
||||||
|
|
||||||
contributors.Add(contributor);
|
contributors.Add(contributor);
|
||||||
@@ -151,7 +151,7 @@ public class MangaDexMetadataProvider(IMangaDexClient mangaDexClient) : IMangaMe
|
|||||||
SourceMangaContributor contributor = new()
|
SourceMangaContributor contributor = new()
|
||||||
{
|
{
|
||||||
Name = artistEntity.Attributes.Name,
|
Name = artistEntity.Attributes.Name,
|
||||||
Role = SourceMangaContributorRole.Artist
|
Role = ContributorRole.Artist
|
||||||
};
|
};
|
||||||
|
|
||||||
contributors.Add(contributor);
|
contributors.Add(contributor);
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ public class MangaNatoWebCrawler(IHtmlLoader htmlLoader) : MangaWebCrawler
|
|||||||
SourceMangaContributor contributor = new()
|
SourceMangaContributor contributor = new()
|
||||||
{
|
{
|
||||||
Name = name,
|
Name = name,
|
||||||
Role = SourceMangaContributorRole.Author
|
Role = ContributorRole.Author
|
||||||
};
|
};
|
||||||
|
|
||||||
contributors.Add(contributor);
|
contributors.Add(contributor);
|
||||||
|
|||||||
@@ -31,6 +31,24 @@ public class MangaPipelineTests(TestDbContextFactory factory) : IClassFixture<Te
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
Genres = ["Action", "Adventure"],
|
Genres = ["Action", "Adventure"],
|
||||||
|
Contributors =
|
||||||
|
[
|
||||||
|
new()
|
||||||
|
{
|
||||||
|
Name = "Manga Author",
|
||||||
|
Role = ContributorRole.Author
|
||||||
|
},
|
||||||
|
new()
|
||||||
|
{
|
||||||
|
Name = "Manga Author",
|
||||||
|
Role = ContributorRole.Artist
|
||||||
|
},
|
||||||
|
new()
|
||||||
|
{
|
||||||
|
Name = "Helper Artist",
|
||||||
|
Role = ContributorRole.Artist
|
||||||
|
}
|
||||||
|
],
|
||||||
Chapters =
|
Chapters =
|
||||||
[
|
[
|
||||||
new()
|
new()
|
||||||
@@ -50,7 +68,7 @@ public class MangaPipelineTests(TestDbContextFactory factory) : IClassFixture<Te
|
|||||||
SourceManga = sourceManga
|
SourceManga = sourceManga
|
||||||
};
|
};
|
||||||
|
|
||||||
await pipeline.RunMetadataAsync(request);
|
await pipeline.RunMetadataAsync(request, CancellationToken.None);
|
||||||
|
|
||||||
context.Mangas.ShouldHaveSingleItem();
|
context.Mangas.ShouldHaveSingleItem();
|
||||||
context.MangaTitles.Count().ShouldBe(2);
|
context.MangaTitles.Count().ShouldBe(2);
|
||||||
@@ -58,6 +76,15 @@ public class MangaPipelineTests(TestDbContextFactory factory) : IClassFixture<Te
|
|||||||
context.MangaTitles.Where(mt => mt.IsPrimary).First().Name.ShouldBe("Fullmetal Alchemist");
|
context.MangaTitles.Where(mt => mt.IsPrimary).First().Name.ShouldBe("Fullmetal Alchemist");
|
||||||
context.MangaTitles.Where(mt => mt.IsPrimary).First().Language.ShouldBe(Language.English);
|
context.MangaTitles.Where(mt => mt.IsPrimary).First().Language.ShouldBe(Language.English);
|
||||||
context.Genres.Count().ShouldBe(2);
|
context.Genres.Count().ShouldBe(2);
|
||||||
|
|
||||||
|
context.MangaContributors.Count().ShouldBe(3);
|
||||||
|
context.MangaContributors.ElementAt(0).Contributor.Name.ShouldBe("Manga Author");
|
||||||
|
context.MangaContributors.ElementAt(0).Role.ShouldBe(ContributorRole.Author);
|
||||||
|
context.MangaContributors.ElementAt(1).Contributor.Name.ShouldBe("Manga Author");
|
||||||
|
context.MangaContributors.ElementAt(1).Role.ShouldBe(ContributorRole.Artist);
|
||||||
|
context.MangaContributors.ElementAt(2).Contributor.Name.ShouldBe("Helper Artist");
|
||||||
|
context.MangaContributors.ElementAt(2).Role.ShouldBe(ContributorRole.Artist);
|
||||||
|
|
||||||
context.SourceChapters.ShouldHaveSingleItem();
|
context.SourceChapters.ShouldHaveSingleItem();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -258,10 +258,10 @@ public class MangaDexMetadataTests
|
|||||||
sourceManga.Contributors.Length.ShouldBe(2);
|
sourceManga.Contributors.Length.ShouldBe(2);
|
||||||
|
|
||||||
sourceManga.Contributors[0].Name.ShouldBe("Norishiro-chan");
|
sourceManga.Contributors[0].Name.ShouldBe("Norishiro-chan");
|
||||||
sourceManga.Contributors[0].Role.ShouldBe(SourceMangaContributorRole.Author);
|
sourceManga.Contributors[0].Role.ShouldBe(ContributorRole.Author);
|
||||||
|
|
||||||
sourceManga.Contributors[1].Name.ShouldBe("Sakana Uozimi");
|
sourceManga.Contributors[1].Name.ShouldBe("Sakana Uozimi");
|
||||||
sourceManga.Contributors[1].Role.ShouldBe(SourceMangaContributorRole.Artist);
|
sourceManga.Contributors[1].Role.ShouldBe(ContributorRole.Artist);
|
||||||
|
|
||||||
sourceManga.Chapters.Count.ShouldBe(3);
|
sourceManga.Chapters.Count.ShouldBe(3);
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using MangaReader.Core.Http;
|
using MangaReader.Core.Common;
|
||||||
|
using MangaReader.Core.Http;
|
||||||
using MangaReader.Core.Metadata;
|
using MangaReader.Core.Metadata;
|
||||||
using MangaReader.Core.Sources.MangaNato.Metadata;
|
using MangaReader.Core.Sources.MangaNato.Metadata;
|
||||||
using MangaReader.Tests.Utilities;
|
using MangaReader.Tests.Utilities;
|
||||||
@@ -37,7 +38,7 @@ public class MangaNatoMetadataTests
|
|||||||
|
|
||||||
SourceMangaContributor[] expectedContributors =
|
SourceMangaContributor[] expectedContributors =
|
||||||
[
|
[
|
||||||
new() { Name = "Nagaoka Taichi", Role = SourceMangaContributorRole.Author }
|
new() { Name = "Nagaoka Taichi", Role = ContributorRole.Author }
|
||||||
];
|
];
|
||||||
|
|
||||||
manga.Contributors.ShouldBeEquivalentTo(expectedContributors);
|
manga.Contributors.ShouldBeEquivalentTo(expectedContributors);
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
using MangaReader.WinUI.ViewModels;
|
using MangaReader.Core.Data;
|
||||||
|
using MangaReader.WinUI.ViewModels;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.UI.Xaml;
|
using Microsoft.UI.Xaml;
|
||||||
using System;
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
namespace MangaReader.WinUI;
|
namespace MangaReader.WinUI;
|
||||||
|
|
||||||
@@ -22,6 +24,11 @@ public partial class App : Application
|
|||||||
services.AddMangaReader();
|
services.AddMangaReader();
|
||||||
|
|
||||||
ServiceProvider = services.BuildServiceProvider();
|
ServiceProvider = services.BuildServiceProvider();
|
||||||
|
|
||||||
|
// Ensure the database is created
|
||||||
|
using var scope = ServiceProvider.CreateScope();
|
||||||
|
var dbContext = scope.ServiceProvider.GetRequiredService<MangaContext>();
|
||||||
|
dbContext.Database.EnsureCreated();
|
||||||
}
|
}
|
||||||
|
|
||||||
public App()
|
public App()
|
||||||
|
|||||||
@@ -51,7 +51,7 @@
|
|||||||
<PackageReference Include="CommunityToolkit.WinUI.Media" 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.Windows.SDK.BuildTools" Version="10.0.26100.4188" />
|
||||||
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.7.250606001" />
|
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.7.250606001" />
|
||||||
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.9" />
|
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.10" />
|
||||||
<PackageReference Include="SkiaSharp" Version="3.119.0" />
|
<PackageReference Include="SkiaSharp" Version="3.119.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
@@ -67,6 +67,9 @@
|
|||||||
<Page Update="Resources\Fonts.xaml">
|
<Page Update="Resources\Fonts.xaml">
|
||||||
<SubType>Designer</SubType>
|
<SubType>Designer</SubType>
|
||||||
</Page>
|
</Page>
|
||||||
|
<Page Update="Views\LibraryView.xaml">
|
||||||
|
<SubType>Designer</SubType>
|
||||||
|
</Page>
|
||||||
<Page Update="Views\SearchView.xaml">
|
<Page Update="Views\SearchView.xaml">
|
||||||
<Generator>MSBuild:Compile</Generator>
|
<Generator>MSBuild:Compile</Generator>
|
||||||
</Page>
|
</Page>
|
||||||
|
|||||||
24
MangaReader.WinUI/ViewModels/LibraryViewModel.cs
Normal file
24
MangaReader.WinUI/ViewModels/LibraryViewModel.cs
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
|
using CommunityToolkit.Mvvm.Input;
|
||||||
|
using MangaReader.Core.Metadata;
|
||||||
|
using MangaReader.Core.Pipeline;
|
||||||
|
using MangaReader.Core.Search;
|
||||||
|
using Microsoft.UI.Dispatching;
|
||||||
|
using Microsoft.UI.Xaml.Media.Imaging;
|
||||||
|
using SixLabors.ImageSharp;
|
||||||
|
using SixLabors.ImageSharp.PixelFormats;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
using System.IO;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Windows.Input;
|
||||||
|
|
||||||
|
namespace MangaReader.WinUI.ViewModels;
|
||||||
|
|
||||||
|
public partial class LibraryViewModel : ViewModelBase
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
using CommunityToolkit.Mvvm.ComponentModel;
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
using CommunityToolkit.Mvvm.Input;
|
using CommunityToolkit.Mvvm.Input;
|
||||||
|
using MangaReader.Core.Metadata;
|
||||||
|
using MangaReader.Core.Pipeline;
|
||||||
using MangaReader.Core.Search;
|
using MangaReader.Core.Search;
|
||||||
using Microsoft.UI.Dispatching;
|
using Microsoft.UI.Dispatching;
|
||||||
using Microsoft.UI.Xaml.Media.Imaging;
|
using Microsoft.UI.Xaml.Media.Imaging;
|
||||||
@@ -16,7 +18,10 @@ using System.Windows.Input;
|
|||||||
|
|
||||||
namespace MangaReader.WinUI.ViewModels;
|
namespace MangaReader.WinUI.ViewModels;
|
||||||
|
|
||||||
public partial class SearchViewModel(IMangaSearchCoordinator searchCoordinator) : ViewModelBase
|
public partial class SearchViewModel(
|
||||||
|
IMangaSearchCoordinator searchCoordinator,
|
||||||
|
IMangaMetadataCoordinator metadataCoordinator,
|
||||||
|
IMangaPipeline pipeline) : ViewModelBase
|
||||||
{
|
{
|
||||||
private readonly DispatcherQueue _dispatcherQueue = DispatcherQueue.GetForCurrentThread();
|
private readonly DispatcherQueue _dispatcherQueue = DispatcherQueue.GetForCurrentThread();
|
||||||
|
|
||||||
@@ -65,6 +70,7 @@ public partial class SearchViewModel(IMangaSearchCoordinator searchCoordinator)
|
|||||||
}
|
}
|
||||||
|
|
||||||
public ICommand SearchCommand => new AsyncRelayCommand(SearchAsync);
|
public ICommand SearchCommand => new AsyncRelayCommand(SearchAsync);
|
||||||
|
//public ICommand ImportCommand => new AsyncRelayCommand(ImportAsync);
|
||||||
|
|
||||||
public async Task SearchAsync()
|
public async Task SearchAsync()
|
||||||
{
|
{
|
||||||
@@ -87,6 +93,8 @@ public partial class SearchViewModel(IMangaSearchCoordinator searchCoordinator)
|
|||||||
|
|
||||||
ObservableMangaSearchResult mangaSearchResult = new()
|
ObservableMangaSearchResult mangaSearchResult = new()
|
||||||
{
|
{
|
||||||
|
Source = searchResult.Source,
|
||||||
|
Url = searchResult.Url,
|
||||||
Title = searchResult.Title,
|
Title = searchResult.Title,
|
||||||
Thumbnail = searchResult.Thumbnail,
|
Thumbnail = searchResult.Thumbnail,
|
||||||
Description = searchResult.Description,
|
Description = searchResult.Description,
|
||||||
@@ -124,10 +132,30 @@ public partial class SearchViewModel(IMangaSearchCoordinator searchCoordinator)
|
|||||||
|
|
||||||
return bitmap;
|
return bitmap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task ImportAsync(ObservableMangaSearchResult searchResult, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
IMangaMetadataProvider metadataProvider = metadataCoordinator.GetProvider(searchResult.Source);
|
||||||
|
SourceManga? sourceManga = await metadataProvider.GetMangaAsync(searchResult.Url, cancellationToken);
|
||||||
|
|
||||||
|
if (sourceManga == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
MangaMetadataPipelineRequest request = new()
|
||||||
|
{
|
||||||
|
SourceName = searchResult.Source,
|
||||||
|
SourceUrl = searchResult.Url,
|
||||||
|
SourceManga = sourceManga,
|
||||||
|
};
|
||||||
|
|
||||||
|
await pipeline.RunMetadataAsync(request, cancellationToken);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public partial class ObservableMangaSearchResult : ObservableObject
|
public partial class ObservableMangaSearchResult : ObservableObject
|
||||||
{
|
{
|
||||||
|
public required string Source { get; init; }
|
||||||
|
public required string Url { get; init; }
|
||||||
public string? Title { get; init; }
|
public string? Title { get; init; }
|
||||||
public string? Description { get; init; }
|
public string? Description { get; init; }
|
||||||
public string? Thumbnail { get; init; }
|
public string? Thumbnail { get; init; }
|
||||||
|
|||||||
41
MangaReader.WinUI/Views/LibraryView.xaml
Normal file
41
MangaReader.WinUI/Views/LibraryView.xaml
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<UserControl
|
||||||
|
x:Class="MangaReader.WinUI.Views.LibraryView"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:local="using:MangaReader.WinUI.Views"
|
||||||
|
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=LibraryViewModel}"
|
||||||
|
d:DataContext="{d:DesignInstance Type=vm:LibraryViewModel, IsDesignTimeCreatable=True}"
|
||||||
|
mc:Ignorable="d">
|
||||||
|
|
||||||
|
<Grid Padding="0" RowSpacing="20">
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto"></RowDefinition>
|
||||||
|
<RowDefinition Height="*"></RowDefinition>
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
<Border HorizontalAlignment="Stretch">
|
||||||
|
<StackPanel Orientation="Horizontal" Spacing="10" HorizontalAlignment="Center" Padding="10">
|
||||||
|
<TextBlock Text="Library" Width="300"></TextBlock>
|
||||||
|
</StackPanel>
|
||||||
|
</Border>
|
||||||
|
|
||||||
|
<!--<ScrollViewer Grid.Row="1" RenderTransformOrigin=".5,.5" Padding="50">
|
||||||
|
<ScrollViewer.RenderTransform>
|
||||||
|
<ScaleTransform ScaleX="1" ScaleY="1" />
|
||||||
|
</ScrollViewer.RenderTransform>
|
||||||
|
<ItemsRepeater ItemsSource="{Binding SearchResults2, Mode=OneWay}" ItemTemplate="{StaticResource MangaSearchResultTemplate}">
|
||||||
|
<ItemsRepeater.Layout>
|
||||||
|
<UniformGridLayout MinRowSpacing="50" MinColumnSpacing="50" ItemsStretch="Fill" MinItemWidth="800"></UniformGridLayout>
|
||||||
|
</ItemsRepeater.Layout>
|
||||||
|
</ItemsRepeater>
|
||||||
|
</ScrollViewer>-->
|
||||||
|
|
||||||
|
</Grid>
|
||||||
|
</UserControl>
|
||||||
19
MangaReader.WinUI/Views/LibraryView.xaml.cs
Normal file
19
MangaReader.WinUI/Views/LibraryView.xaml.cs
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
using MangaReader.WinUI.ViewModels;
|
||||||
|
using Microsoft.UI.Xaml;
|
||||||
|
using Microsoft.UI.Xaml.Controls;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace MangaReader.WinUI.Views;
|
||||||
|
|
||||||
|
public sealed partial class LibraryView : UserControl
|
||||||
|
{
|
||||||
|
private readonly LibraryViewModel viewModel;
|
||||||
|
|
||||||
|
public LibraryView()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
|
||||||
|
viewModel = (LibraryViewModel)DataContext;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -34,6 +34,7 @@
|
|||||||
<RowDefinition Height="Auto"></RowDefinition>
|
<RowDefinition Height="Auto"></RowDefinition>
|
||||||
<RowDefinition Height="Auto"></RowDefinition>
|
<RowDefinition Height="Auto"></RowDefinition>
|
||||||
<RowDefinition Height="*"></RowDefinition>
|
<RowDefinition Height="*"></RowDefinition>
|
||||||
|
<RowDefinition Height="Auto"></RowDefinition>
|
||||||
</Grid.RowDefinitions>
|
</Grid.RowDefinitions>
|
||||||
<TextBlock Grid.Row="0" Text="{x:Bind Title, Mode=OneTime}" FontSize="24" FontFamily="{StaticResource PoppinsSemiBold}" TextWrapping="Wrap"></TextBlock>
|
<TextBlock Grid.Row="0" Text="{x:Bind Title, Mode=OneTime}" FontSize="24" FontFamily="{StaticResource PoppinsSemiBold}" TextWrapping="Wrap"></TextBlock>
|
||||||
<ItemsControl Grid.Row="1" ItemsSource="{x:Bind Genres, Mode=OneTime}" ItemTemplate="{StaticResource GenreTemplate}">
|
<ItemsControl Grid.Row="1" ItemsSource="{x:Bind Genres, Mode=OneTime}" ItemTemplate="{StaticResource GenreTemplate}">
|
||||||
@@ -43,10 +44,17 @@
|
|||||||
</ItemsPanelTemplate>
|
</ItemsPanelTemplate>
|
||||||
</ItemsControl.ItemsPanel>
|
</ItemsControl.ItemsPanel>
|
||||||
</ItemsControl>
|
</ItemsControl>
|
||||||
<ScrollViewer Grid.Row="3">
|
<ScrollViewer Grid.Row="2">
|
||||||
<TextBlock Text="{x:Bind Description}" Foreground="{StaticResource TextFillColorSecondaryBrush}" FontSize="16" TextWrapping="Wrap" LineStackingStrategy="BlockLineHeight" LineHeight="22"></TextBlock>
|
<TextBlock Text="{x:Bind Description}" Foreground="{StaticResource TextFillColorSecondaryBrush}" FontSize="16" TextWrapping="Wrap" LineStackingStrategy="BlockLineHeight" LineHeight="22"></TextBlock>
|
||||||
</ScrollViewer>
|
</ScrollViewer>
|
||||||
|
<StackPanel Grid.Row="3" Orientation="Horizontal" HorizontalAlignment="Right">
|
||||||
|
<Button
|
||||||
|
Content="Import"
|
||||||
|
MinWidth="100"
|
||||||
|
Tag="{x:Bind}"
|
||||||
|
Click="Button_Click">
|
||||||
|
</Button>
|
||||||
|
</StackPanel>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
|
|||||||
@@ -1,12 +1,30 @@
|
|||||||
using MangaReader.Core.Search;
|
using MangaReader.WinUI.ViewModels;
|
||||||
|
using Microsoft.UI.Xaml;
|
||||||
using Microsoft.UI.Xaml.Controls;
|
using Microsoft.UI.Xaml.Controls;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace MangaReader.WinUI.Views;
|
namespace MangaReader.WinUI.Views;
|
||||||
|
|
||||||
public sealed partial class SearchView : UserControl
|
public sealed partial class SearchView : UserControl
|
||||||
{
|
{
|
||||||
|
private readonly SearchViewModel viewModel;
|
||||||
|
|
||||||
public SearchView()
|
public SearchView()
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
|
||||||
|
viewModel = (SearchViewModel)DataContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void Button_Click(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
if (sender is not Button button)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (button.Tag is not ObservableMangaSearchResult searchResult)
|
||||||
|
return;
|
||||||
|
|
||||||
|
await viewModel.ImportAsync(searchResult, CancellationToken.None);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user