|
|
@@ -44,7 +44,6 @@ import { canStartTeacherLedLearning, isTeacherLedLearningPath } from '../../core
|
|
|
import { isLearningStage, learningReturnUrl } from '../../core/learning-navigation';
|
|
|
import { PlatformService } from '../../core/platform.service';
|
|
|
import { SessionService } from '../../core/session.service';
|
|
|
-import { LiveDataRefreshService } from '../../core/live-data-refresh.service';
|
|
|
import { nextReviewWordOrder } from '../../core/review-word-order';
|
|
|
|
|
|
type SourceItem = Record<string, unknown>;
|
|
|
@@ -109,6 +108,7 @@ const WORD_CATALOG_CATEGORIES = [
|
|
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
|
})
|
|
|
export class LearningPageComponent implements OnInit, OnDestroy {
|
|
|
+ private static readonly reviewCacheByApi = new WeakMap<ApiService, { identity: string; pages: Map<string, ReviewPage> }>();
|
|
|
readonly meta = inject(ActivatedRoute).snapshot.data['meta'] as PageMeta;
|
|
|
readonly nodes = signal<SourceItem[]>([]);
|
|
|
readonly catalogCategories = WORD_CATALOG_CATEGORIES;
|
|
|
@@ -278,14 +278,12 @@ export class LearningPageComponent implements OnInit, OnDestroy {
|
|
|
private readonly api = inject(ApiService);
|
|
|
private readonly session = inject(SessionService);
|
|
|
private readonly platform = inject(PlatformService);
|
|
|
- private readonly liveData = inject(LiveDataRefreshService);
|
|
|
private readonly wordPreviewPageSize = 30;
|
|
|
private examTimer: ReturnType<typeof setInterval> | null = null;
|
|
|
private antiStartedAt = Date.now();
|
|
|
private calendarRequest?: Subscription;
|
|
|
private choiceOptionsRequest?: Subscription;
|
|
|
private choiceOptionsVersion = 0;
|
|
|
- private stopLiveRefresh?: () => void;
|
|
|
|
|
|
ngOnInit(): void {
|
|
|
if (isTeacherLedLearningPath(this.meta.path) && !this.isReadOnlyWordView && !this.isReadOnlySelectionPage && !this.canStartPointReading && !this.isNewWordSelfStudyStage) {
|
|
|
@@ -298,20 +296,12 @@ export class LearningPageComponent implements OnInit, OnDestroy {
|
|
|
return;
|
|
|
}
|
|
|
this.load();
|
|
|
- if (this.isCalendarPage) {
|
|
|
- this.stopLiveRefresh = this.liveData.watch({
|
|
|
- scopes: ['review', 'account'],
|
|
|
- studentId: () => Number(this.memoryStudentId() || this.session.user()?.userId) || undefined,
|
|
|
- onRefresh: () => this.loadCalendar(),
|
|
|
- });
|
|
|
- }
|
|
|
}
|
|
|
|
|
|
ngOnDestroy(): void {
|
|
|
this.calendarRequestId++;this.calendarRequest?.unsubscribe();this.selectionRequest?.unsubscribe();
|
|
|
this.choiceOptionsVersion++;
|
|
|
this.choiceOptionsRequest?.unsubscribe();
|
|
|
- this.stopLiveRefresh?.();
|
|
|
this.stopExamTimer();
|
|
|
this.platform.stopAudio();
|
|
|
}
|
|
|
@@ -953,7 +943,10 @@ export class LearningPageComponent implements OnInit, OnDestroy {
|
|
|
content: JSON.stringify({ ModelID: 60, nodeId: 388, status: 99, GeneralID: this.query('gid'), UpDateTime: now }),
|
|
|
addon: JSON.stringify({ fxzt: 1, wcsj: now }),
|
|
|
}).pipe(finalize(() => this.submitting.set(false))).subscribe({
|
|
|
- next: () => void this.router.navigateByUrl('/pages/stu/stu_calendar'),
|
|
|
+ next: () => {
|
|
|
+ this.reviewCache().clear();
|
|
|
+ void this.router.navigateByUrl('/pages/stu/stu_calendar');
|
|
|
+ },
|
|
|
error: (error: ApiError) => this.platform.toast(error.message, 'error'),
|
|
|
});
|
|
|
}
|
|
|
@@ -1777,18 +1770,39 @@ export class LearningPageComponent implements OnInit, OnDestroy {
|
|
|
}
|
|
|
|
|
|
turnReviewPage(delta:number):void{this.reviewListPage.update(p=>Math.max(1,Math.min(this.reviewPageData()?.page.pageCount||1,p+delta)));this.loadCalendar();}
|
|
|
- retryCalendar(): void { this.loadCalendar(); }
|
|
|
+ retryCalendar(): void { this.loadCalendar(false, true); }
|
|
|
|
|
|
- private loadCalendar(initial = false): void {
|
|
|
+ private reviewCache(): Map<string, ReviewPage> {
|
|
|
+ const identity = `${this.session.token()}|${this.session.user()?.userId ?? ''}`;
|
|
|
+ let cache = LearningPageComponent.reviewCacheByApi.get(this.api);
|
|
|
+ if (!cache || cache.identity !== identity) {
|
|
|
+ cache = { identity, pages: new Map() };
|
|
|
+ LearningPageComponent.reviewCacheByApi.set(this.api, cache);
|
|
|
+ }
|
|
|
+ return cache.pages;
|
|
|
+ }
|
|
|
+
|
|
|
+ private loadCalendar(initial = false, force = false): void {
|
|
|
const requestId=++this.calendarRequestId;
|
|
|
this.calendarRequest?.unsubscribe();
|
|
|
this.calendarError.set('');
|
|
|
+ const date = this.memorySelectedDate() || this.dateOnly();
|
|
|
+ const key = `${this.memoryStudentId()}|${date}|${this.reviewListPage()}`;
|
|
|
+ const cache = this.reviewCache();
|
|
|
+ const cached = force ? undefined : cache.get(key);
|
|
|
+ if (cached) {
|
|
|
+ this.reviewPageData.set(cached);this.calendarItems.set(cached.items);
|
|
|
+ if(!this.memorySelectedDate())this.memorySelectedDate.set(date);
|
|
|
+ this.loading.set(false);this.calendarLoading.set(false);
|
|
|
+ return;
|
|
|
+ }
|
|
|
if (initial) this.loading.set(true); else this.calendarLoading.set(true);
|
|
|
- this.calendarRequest = this.api.post<ReviewPage>('app_review_page',{uid:this.session.user()?.userId,studentId:this.memoryStudentId()||undefined,date:this.memorySelectedDate()||this.dateOnly(),page:this.reviewListPage(),pageSize:20}).pipe(finalize(()=>{
|
|
|
+ this.calendarRequest = this.api.post<ReviewPage>('app_review_page',{uid:this.session.user()?.userId,studentId:this.memoryStudentId()||undefined,date,page:this.reviewListPage(),pageSize:20}).pipe(finalize(()=>{
|
|
|
if(requestId!==this.calendarRequestId)return;
|
|
|
if(initial)this.loading.set(false);else this.calendarLoading.set(false);
|
|
|
})).subscribe({next:({result})=>{
|
|
|
if(requestId!==this.calendarRequestId)return;
|
|
|
+ cache.set(key,result);
|
|
|
this.reviewPageData.set(result);this.calendarItems.set(result.items);
|
|
|
if(!this.memorySelectedDate())this.memorySelectedDate.set(this.dateOnly());
|
|
|
},error:(e:ApiError)=>{
|