|
|
@@ -1,9 +1,9 @@
|
|
|
import { DropdownDirective } from '../../shared/dropdown.directive';
|
|
|
import { CommonModule } from '@angular/common';
|
|
|
-import { ChangeDetectionStrategy, Component, inject, OnInit, signal } from '@angular/core';
|
|
|
+import { ChangeDetectionStrategy, Component, inject, OnDestroy, OnInit, signal } from '@angular/core';
|
|
|
import { FormsModule } from '@angular/forms';
|
|
|
import { ActivatedRoute, Router } from '@angular/router';
|
|
|
-import { ArrowLeft, BookOpen, ChevronRight, Clock3, History, LoaderCircle, LucideAngularModule, Sparkles, X } from 'lucide-angular';
|
|
|
+import { ArrowLeft, BookOpen, ChevronRight, Clock3, LoaderCircle, LucideAngularModule, Sparkles, X } from 'lucide-angular';
|
|
|
import { PlatformService } from '../../core/platform.service';
|
|
|
import { SessionService } from '../../core/session.service';
|
|
|
import { EnglishReadingService } from './english-reading.service';
|
|
|
@@ -20,17 +20,18 @@ import { normalizeReadingParams, normalizeReadingReturnUrl, READING_LEVELS, READ
|
|
|
styleUrl: './reading-setup.component.scss',
|
|
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
|
})
|
|
|
-export class ReadingSetupComponent implements OnInit {
|
|
|
+export class ReadingSetupComponent implements OnInit, OnDestroy {
|
|
|
readonly levels = READING_LEVELS;
|
|
|
readonly types = READING_TYPES;
|
|
|
readonly themes = READING_THEMES;
|
|
|
- readonly icons = { ArrowLeft, BookOpen, ChevronRight, Clock3, History, LoaderCircle, Sparkles, X };
|
|
|
+ readonly icons = { ArrowLeft, BookOpen, ChevronRight, Clock3, LoaderCircle, Sparkles, X };
|
|
|
readonly busy = signal(false);
|
|
|
readonly historyOpen = signal(false);
|
|
|
readonly historyLoading = signal(false);
|
|
|
readonly historyLoadingMore = signal(false);
|
|
|
readonly historyHasMore = signal(true);
|
|
|
readonly historyItems = signal<ReadingHistoryEntry[]>([]);
|
|
|
+ readonly historyCount = signal<number | null>(null);
|
|
|
readonly nameDirty = signal(false);
|
|
|
|
|
|
nickname = '';
|
|
|
@@ -41,6 +42,8 @@ export class ReadingSetupComponent implements OnInit {
|
|
|
theme = '随机';
|
|
|
private historyPageIndex = 0;
|
|
|
private readonly historyPageSize = 10;
|
|
|
+ private historyCountTimer?: ReturnType<typeof setTimeout>;
|
|
|
+ private historyCountRequestId = 0;
|
|
|
private routeParams!: ReadingRouteParams;
|
|
|
|
|
|
private readonly route = inject(ActivatedRoute);
|
|
|
@@ -74,6 +77,31 @@ export class ReadingSetupComponent implements OnInit {
|
|
|
this.type = params.type || '随机';
|
|
|
this.theme = params.theme || '随机';
|
|
|
this.level = params.level || this.level;
|
|
|
+ void this.refreshHistoryCount();
|
|
|
+ }
|
|
|
+
|
|
|
+ ngOnDestroy(): void {
|
|
|
+ clearTimeout(this.historyCountTimer);
|
|
|
+ this.historyCountRequestId += 1;
|
|
|
+ }
|
|
|
+
|
|
|
+ onVocabularyChange(): void {
|
|
|
+ clearTimeout(this.historyCountTimer);
|
|
|
+ this.historyCountRequestId += 1;
|
|
|
+ this.historyCount.set(null);
|
|
|
+ this.historyCountTimer = setTimeout(() => void this.refreshHistoryCount(), 350);
|
|
|
+ }
|
|
|
+
|
|
|
+ private async refreshHistoryCount(): Promise<void> {
|
|
|
+ const requestId = ++this.historyCountRequestId;
|
|
|
+ const words = splitWords(this.vocabulary);
|
|
|
+ try {
|
|
|
+ await this.ensureUser();
|
|
|
+ const count = await this.reading.countArticles(words);
|
|
|
+ if (requestId === this.historyCountRequestId) this.historyCount.set(count);
|
|
|
+ } catch {
|
|
|
+ if (requestId === this.historyCountRequestId) this.historyCount.set(null);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
markNameDirty(): void { this.nameDirty.set(true); }
|
|
|
@@ -143,6 +171,7 @@ export class ReadingSetupComponent implements OnInit {
|
|
|
this.historyItems.set(items);
|
|
|
this.historyHasMore.set(items.length === this.historyPageSize);
|
|
|
this.historyPageIndex = 1;
|
|
|
+ void this.refreshHistoryCount();
|
|
|
} catch (error) { this.showError(error); }
|
|
|
finally { this.historyLoading.set(false); }
|
|
|
}
|