From 0f4fd6a36be8f12301ae5ff1e761e9ecb49106a0 Mon Sep 17 00:00:00 2001 From: Tangent Chang Date: Tue, 21 May 2024 18:22:30 +0800 Subject: [PATCH] feat: plot speeds in chart --- .../statistics-page.component.html | 15 ++++--- .../statistics-page.component.ts | 45 ++++++++++++++++--- src/app/stores/lesson.store.ts | 2 +- src/app/utils/computed-async.utils.ts | 27 +++++++++++ 4 files changed, 76 insertions(+), 13 deletions(-) create mode 100644 src/app/utils/computed-async.utils.ts diff --git a/src/app/pages/statistics-page/statistics-page.component.html b/src/app/pages/statistics-page/statistics-page.component.html index 3c757a3..5c5a9dc 100644 --- a/src/app/pages/statistics-page/statistics-page.component.html +++ b/src/app/pages/statistics-page/statistics-page.component.html @@ -1,6 +1,9 @@ - +@if (chartOptions(); as chartOptions) { + +} diff --git a/src/app/pages/statistics-page/statistics-page.component.ts b/src/app/pages/statistics-page/statistics-page.component.ts index 8bc4de7..a62f3ae 100644 --- a/src/app/pages/statistics-page/statistics-page.component.ts +++ b/src/app/pages/statistics-page/statistics-page.component.ts @@ -1,7 +1,13 @@ -import { ChangeDetectionStrategy, Component } from '@angular/core'; +import { ChangeDetectionStrategy, Component, computed } from '@angular/core'; +import { liveQuery } from 'dexie'; import { HighchartsChartModule } from 'highcharts-angular'; import * as Highcharts from 'highcharts/highstock'; import theme from 'highcharts/themes/high-contrast-dark'; +import { Observable } from 'rxjs'; +import { TOPICS } from 'src/app/data/topics'; +import { db } from 'src/app/db'; +import { KeyRecord } from 'src/app/models/key-record.models'; +import { computedAsync } from 'src/app/utils/computed-async.utils'; theme(Highcharts); @Component({ @@ -12,9 +18,36 @@ theme(Highcharts); changeDetection: ChangeDetectionStrategy.OnPush, }) export class StatisticsPageComponent { - Highcharts: typeof Highcharts = Highcharts; // required - chartConstructor: string = 'stockChart'; // optional string, defaults to 'chart' - chartOptions: Highcharts.Options = { - series: [{ type: 'line', data: [1, 2] }], - }; // required + Highcharts: typeof Highcharts = Highcharts; + chartConstructor: string = 'stockChart'; + updateFlag = false; + + keyRecords = computedAsync(() => { + return liveQuery(() => db.keyRecords.toArray()) as any as Observable< + KeyRecord[] + >; + }); + chartOptions = computed(() => { + const keyRecords = this.keyRecords(); + if (!keyRecords) { + return null; + } + const series = TOPICS.map((t) => ({ + type: 'line' as const, + data: keyRecords + .filter((k) => k.cpm && k.topicId === t.id) + .map((k) => [k.timestamp, k.cpm]), + name: t.name, + })).filter((s) => s.data.length > 0); + return { + legend: { enabled: true }, + scrollbar: { enabled: false }, + series, + yAxis: { + title: { + text: 'CPM', + }, + }, + }; + }); } diff --git a/src/app/stores/lesson.store.ts b/src/app/stores/lesson.store.ts index bbfbb1d..5cad91f 100644 --- a/src/app/stores/lesson.store.ts +++ b/src/app/stores/lesson.store.ts @@ -83,7 +83,7 @@ export const LessonStore = signalStore( } db.keyRecords.add({ ...commonKeyRecord, - isCorrect: false, + isCorrect: true, intervalToPreviousCorrectKey: keyInterval, cpm: keyInterval ? Math.floor((60 * 1000) / keyInterval) : null, combo: state.combo + 1, diff --git a/src/app/utils/computed-async.utils.ts b/src/app/utils/computed-async.utils.ts new file mode 100644 index 0000000..bcd6bae --- /dev/null +++ b/src/app/utils/computed-async.utils.ts @@ -0,0 +1,27 @@ +import { Signal, effect, signal } from '@angular/core'; +import { Observable, Subscription } from 'rxjs'; + +export function computedAsync( + computation: () => Observable, +): Signal & { recompute: () => void } { + const sig = signal(null); + + // Save current subscription to be able to unsubscribe + let subscription: Subscription; + + // Create an arrow function that contains the signal updating logic + const recompute = () => { + sig.set(null); + // Before making the new subscription, unsub from the previous one + if (subscription && !subscription.closed) { + subscription.unsubscribe(); + } + const observable = computation(); + subscription = observable.subscribe((result) => sig.set(result)); + }; + + effect(recompute, { allowSignalWrites: true }); + + // Add the recompute function to the returned signal, so that it can be called from the outside + return Object.assign(sig, { recompute }); +}