Fix TOC open crash from infinite ayah reload loop.
build-and-deploy / deploy (push) Successful in 4m4s
build-and-deploy / deploy (push) Successful in 4m4s
Load the surah list once on open, stop progress ticks from re-rendering the dock, and keep TOC row markup stable for Blazor diffs. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -56,10 +56,9 @@
|
||||
|
||||
<div class="sheet-track-meta" dir="rtl">
|
||||
<div class="sheet-track-name">@SurahLabel — آیه @AyahNumber</div>
|
||||
@if (SurahAyahCount > 0)
|
||||
{
|
||||
<div class="sheet-track-index">@AyahNumber / @SurahAyahCount</div>
|
||||
}
|
||||
<div class="sheet-track-index">
|
||||
@(SurahAyahCount > 0 ? $"{AyahNumber} / {SurahAyahCount}" : "\u00A0")
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="sheet-actions" dir="rtl" @onclick:stopPropagation="true">
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
|
||||
@if (Session.IsTocOpen && Session.HasTrack)
|
||||
{
|
||||
<div class="toc-sheet" dir="rtl">
|
||||
<div class="toc-sheet" dir="rtl" @key="tocKey">
|
||||
<div class="toc-header">
|
||||
<button type="button" class="sheet-icon-btn" @onclick="Session.CloseToc" aria-label="بازگشت">›</button>
|
||||
<button type="button" class="sheet-icon-btn" @onclick="Close" aria-label="بازگشت">›</button>
|
||||
<div class="toc-header-text">
|
||||
<strong>سوره @Session.SurahNameFa</strong>
|
||||
<span class="muted">@Session.TafsirTitle</span>
|
||||
@@ -30,12 +30,14 @@
|
||||
@foreach (var a in ayahs)
|
||||
{
|
||||
var active = a.AyahNumber == Session.AyahNumber;
|
||||
<li class="toc-row @(active ? "active" : null)">
|
||||
<li @key="a.AyahNumber" class="toc-row @(active ? "active" : null)">
|
||||
<button type="button" class="toc-row-btn" @onclick="() => Select(a.AyahNumber)">
|
||||
@if (active)
|
||||
{
|
||||
<span class="toc-play-dot" aria-hidden="true">▶</span>
|
||||
}
|
||||
<span class="toc-play-slot" aria-hidden="true">
|
||||
@if (active)
|
||||
{
|
||||
<span class="toc-play-dot">▶</span>
|
||||
}
|
||||
</span>
|
||||
<span class="toc-row-main">
|
||||
<span class="toc-row-title">آیه @a.AyahNumber</span>
|
||||
@if (!string.IsNullOrWhiteSpace(a.TextFaPreview))
|
||||
@@ -43,9 +45,18 @@
|
||||
<span class="toc-row-preview">@a.TextFaPreview</span>
|
||||
}
|
||||
<span class="toc-row-flags">
|
||||
@if (a.IsFavorite) { <span title="علاقه">♥</span> }
|
||||
@if (a.IsBookmarked) { <span title="نشان">⚑</span> }
|
||||
@if (!a.HasAudio) { <span class="muted">بدون صوت</span> }
|
||||
@if (a.IsFavorite)
|
||||
{
|
||||
<span title="علاقه">♥</span>
|
||||
}
|
||||
@if (a.IsBookmarked)
|
||||
{
|
||||
<span title="نشان">⚑</span>
|
||||
}
|
||||
@if (!a.HasAudio)
|
||||
{
|
||||
<span class="muted">بدون صوت</span>
|
||||
}
|
||||
</span>
|
||||
</span>
|
||||
<span class="toc-row-num">@a.AyahNumber</span>
|
||||
@@ -60,30 +71,42 @@
|
||||
@code {
|
||||
private List<AyahListItemDto> ayahs = new();
|
||||
private bool loading;
|
||||
private bool loadInProgress;
|
||||
private bool wasOpen;
|
||||
private int loadedForSurah = -1;
|
||||
private string loadedSlug = "";
|
||||
|
||||
protected override void OnInitialized() => Session.Changed += OnChanged;
|
||||
private string tocKey => $"{Session.TafsirSlug}:{Session.SurahNumber}";
|
||||
|
||||
protected override async Task OnParametersSetAsync()
|
||||
{
|
||||
await EnsureLoadedAsync();
|
||||
}
|
||||
protected override void OnInitialized() => Session.Changed += OnSessionChanged;
|
||||
|
||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||
{
|
||||
if (Session.IsTocOpen)
|
||||
await EnsureLoadedAsync();
|
||||
// Load only on open transition — never on every render
|
||||
if (Session.IsTocOpen && Session.HasTrack && !wasOpen)
|
||||
{
|
||||
wasOpen = true;
|
||||
await LoadAsync(force: true);
|
||||
}
|
||||
else if (!Session.IsTocOpen)
|
||||
{
|
||||
wasOpen = false;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task EnsureLoadedAsync()
|
||||
private async Task LoadAsync(bool force)
|
||||
{
|
||||
if (!Session.IsTocOpen || !Session.HasTrack) return;
|
||||
if (loadedForSurah == Session.SurahNumber && loadedSlug == Session.TafsirSlug && ayahs.Count > 0)
|
||||
if (loadInProgress) return;
|
||||
if (!force
|
||||
&& loadedForSurah == Session.SurahNumber
|
||||
&& loadedSlug == Session.TafsirSlug)
|
||||
return;
|
||||
|
||||
loadInProgress = true;
|
||||
loading = true;
|
||||
await InvokeAsync(StateHasChanged);
|
||||
|
||||
try
|
||||
{
|
||||
ayahs = (await Session.GetCurrentSurahAyahsAsync()).ToList();
|
||||
@@ -93,26 +116,38 @@
|
||||
catch
|
||||
{
|
||||
ayahs = new();
|
||||
loadedForSurah = Session.SurahNumber;
|
||||
loadedSlug = Session.TafsirSlug;
|
||||
}
|
||||
finally
|
||||
{
|
||||
loading = false;
|
||||
loadInProgress = false;
|
||||
await InvokeAsync(StateHasChanged);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task Select(int ayahNumber) => await Session.PlayAyahAsync(ayahNumber);
|
||||
|
||||
private async void OnChanged()
|
||||
private void Close() => Session.CloseToc();
|
||||
|
||||
private void OnSessionChanged()
|
||||
{
|
||||
if (Session.IsTocOpen &&
|
||||
(loadedForSurah != Session.SurahNumber || loadedSlug != Session.TafsirSlug))
|
||||
_ = InvokeAsync(async () =>
|
||||
{
|
||||
loadedForSurah = -1;
|
||||
await EnsureLoadedAsync();
|
||||
}
|
||||
await InvokeAsync(StateHasChanged);
|
||||
// Only reload when TOC is open and surah/slug actually changed
|
||||
if (Session.IsTocOpen && Session.HasTrack
|
||||
&& (loadedForSurah != Session.SurahNumber || loadedSlug != Session.TafsirSlug)
|
||||
&& !loadInProgress)
|
||||
{
|
||||
await LoadAsync(force: true);
|
||||
}
|
||||
else
|
||||
{
|
||||
StateHasChanged();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void Dispose() => Session.Changed -= OnChanged;
|
||||
public void Dispose() => Session.Changed -= OnSessionChanged;
|
||||
}
|
||||
|
||||
@@ -202,22 +202,16 @@ public sealed class PlayerSessionService
|
||||
played += (DateTimeOffset.UtcNow - start).TotalSeconds;
|
||||
|
||||
if (played < PersistAfterPlayedSeconds)
|
||||
{
|
||||
Notify();
|
||||
return;
|
||||
}
|
||||
|
||||
var now = DateTimeOffset.UtcNow;
|
||||
if (_persistedForTrack && (now - _lastPersistAt).TotalSeconds < 4)
|
||||
{
|
||||
Notify();
|
||||
return;
|
||||
}
|
||||
|
||||
_persistedForTrack = true;
|
||||
_lastPersistAt = now;
|
||||
await PersistAsync(position);
|
||||
Notify();
|
||||
// No Notify — position ticks must not re-render dock/TOC
|
||||
}
|
||||
|
||||
public async Task ToggleFavoriteAsync()
|
||||
|
||||
@@ -518,19 +518,22 @@ button, input { font: inherit; }
|
||||
.toc-row-btn {
|
||||
width: 100%; border: 0; background: transparent; cursor: pointer;
|
||||
display: grid;
|
||||
grid-template-columns: auto 1fr auto;
|
||||
grid-template-columns: 36px 1fr auto;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
padding: 0.95rem 1rem;
|
||||
text-align: right;
|
||||
color: inherit;
|
||||
}
|
||||
.toc-play-slot {
|
||||
width: 36px; height: 36px;
|
||||
display: grid; place-items: center;
|
||||
}
|
||||
.toc-play-dot {
|
||||
width: 36px; height: 36px; border-radius: 50%;
|
||||
background: var(--brand); color: #fff;
|
||||
display: grid; place-items: center; font-size: 0.85rem;
|
||||
}
|
||||
.toc-row:not(.active) .toc-row-btn { grid-template-columns: 1fr auto; }
|
||||
.toc-row-main { display: grid; gap: 0.2rem; min-width: 0; }
|
||||
.toc-row-title { font-weight: 700; color: var(--ink); }
|
||||
.toc-row-preview {
|
||||
|
||||
Reference in New Issue
Block a user