Initial TafsirQuran app with Gitea CI/CD deploy to k3s.
build-and-deploy / deploy (push) Successful in 10m50s
build-and-deploy / deploy (push) Successful in 10m50s
Blazor UI + ASP.NET API + Postgres; push to main builds images and rolls out namespace tafsirquran.
This commit is contained in:
@@ -0,0 +1,148 @@
|
||||
@page "/tafsir/{Slug}"
|
||||
@inject TafsirApiClient Api
|
||||
@inject IDeviceIdStore DeviceStore
|
||||
|
||||
<section class="tafsir-page">
|
||||
<header class="page-header">
|
||||
<h1>@title</h1>
|
||||
<p class="muted">@author</p>
|
||||
</header>
|
||||
|
||||
@if (loading)
|
||||
{
|
||||
<p class="muted">در حال بارگذاری...</p>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="episode-layout">
|
||||
<div class="episode-list">
|
||||
@foreach (var ep in episodes)
|
||||
{
|
||||
<button type="button"
|
||||
class="episode-row @(selected?.Id == ep.Id ? "active" : null)"
|
||||
@onclick="() => SelectAsync(ep.Id)">
|
||||
<span class="ep-title">@ep.Title</span>
|
||||
<span class="ep-meta">@ep.SurahNameFa — آیه @ep.AyahNumber</span>
|
||||
</button>
|
||||
}
|
||||
</div>
|
||||
|
||||
<div class="player-panel">
|
||||
@if (detail is null)
|
||||
{
|
||||
<p class="muted">یک آیه را انتخاب کنید</p>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="ayah-block">
|
||||
<p class="ayah-ar">@detail.AyahTextAr</p>
|
||||
@if (!string.IsNullOrWhiteSpace(detail.AyahTextFa))
|
||||
{
|
||||
<p class="ayah-fa">@detail.AyahTextFa</p>
|
||||
}
|
||||
</div>
|
||||
|
||||
<AudioPlayer @key="detail.Id"
|
||||
Episode="detail"
|
||||
InitialPosition="initialPosition"
|
||||
IsFavorite="isFavorite"
|
||||
IsBookmarked="isBookmarked"
|
||||
OnFavoriteToggle="ToggleFavorite"
|
||||
OnBookmarkToggle="ToggleBookmark"
|
||||
OnProgress="SaveProgress" />
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</section>
|
||||
|
||||
@code {
|
||||
[Parameter] public string Slug { get; set; } = "";
|
||||
|
||||
private string title = "تفسیر";
|
||||
private string author = "";
|
||||
private bool loading = true;
|
||||
private string? loadedForSlug;
|
||||
private List<EpisodeListItemDto> episodes = [];
|
||||
private EpisodeListItemDto? selected;
|
||||
private EpisodeDetailDto? detail;
|
||||
private double initialPosition;
|
||||
private bool isFavorite;
|
||||
private bool isBookmarked;
|
||||
private string deviceId = "";
|
||||
private bool ready;
|
||||
|
||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||
{
|
||||
if (firstRender) ready = true;
|
||||
if (!ready || string.IsNullOrWhiteSpace(Slug) || loadedForSlug == Slug) return;
|
||||
|
||||
loading = true;
|
||||
loadedForSlug = Slug;
|
||||
deviceId = await DeviceStore.GetOrCreateAsync();
|
||||
|
||||
try
|
||||
{
|
||||
var tafsirs = await Api.GetTafsirsAsync();
|
||||
var tafsir = tafsirs.FirstOrDefault(t => t.Slug == Slug);
|
||||
title = tafsir?.Title ?? "تفسیر";
|
||||
author = tafsir?.Author ?? "";
|
||||
|
||||
episodes = (await Api.GetEpisodesAsync(Slug)).ToList();
|
||||
|
||||
var progress = await Api.GetProgressAsync(deviceId);
|
||||
var startId = progress?.Episode?.Id;
|
||||
if (startId is null || episodes.All(e => e.Id != startId))
|
||||
startId = episodes.FirstOrDefault()?.Id;
|
||||
|
||||
if (startId is Guid id)
|
||||
{
|
||||
initialPosition = progress?.EpisodeId == id ? progress.PositionSeconds : 0;
|
||||
await SelectAsync(id, keepPosition: true);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
loading = false;
|
||||
await InvokeAsync(StateHasChanged);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task SelectAsync(Guid id, bool keepPosition = false)
|
||||
{
|
||||
selected = episodes.FirstOrDefault(e => e.Id == id);
|
||||
detail = await Api.GetEpisodeAsync(id);
|
||||
if (!keepPosition) initialPosition = 0;
|
||||
|
||||
var favorites = await Api.GetFavoritesAsync(deviceId);
|
||||
var bookmarks = await Api.GetBookmarksAsync(deviceId);
|
||||
isFavorite = favorites.Any(f => f.EpisodeId == id);
|
||||
isBookmarked = bookmarks.Any(b => b.EpisodeId == id);
|
||||
}
|
||||
|
||||
private async Task ToggleFavorite()
|
||||
{
|
||||
if (detail is null) return;
|
||||
if (isFavorite)
|
||||
await Api.RemoveFavoriteAsync(detail.Id, deviceId);
|
||||
else
|
||||
await Api.AddFavoriteAsync(new ToggleEpisodeRequest(detail.Id, deviceId));
|
||||
isFavorite = !isFavorite;
|
||||
}
|
||||
|
||||
private async Task ToggleBookmark()
|
||||
{
|
||||
if (detail is null) return;
|
||||
if (isBookmarked)
|
||||
await Api.RemoveBookmarkAsync(detail.Id, deviceId);
|
||||
else
|
||||
await Api.AddBookmarkAsync(new ToggleEpisodeRequest(detail.Id, deviceId));
|
||||
isBookmarked = !isBookmarked;
|
||||
}
|
||||
|
||||
private async Task SaveProgress(double position)
|
||||
{
|
||||
if (detail is null) return;
|
||||
await Api.SaveProgressAsync(new UpsertProgressRequest(detail.Id, position, deviceId));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user