mirror of
https://github.com/hectorqin/reader.git
synced 2026-11-12 05:50:54 +08:00
Reading time is counted by the reader: only while the page is visible and the reader has turned, scrolled or tapped within three minutes, or while 朗读 plays. It is kept per book per 15-minute UTC slot and per device install, saved locally until sent, and stored as MAX(stored, reported) in the new reading_time table, so a replayed report never counts twice. Media time is derived on the server from the progress saves the web player and Android Media3 already send: the smaller of the wall-clock gap and the position advance between two saves of one session, gaps over 30s ignored. It is stored in media.db (media_watch_time), so neither client changes. 我的 → 时长统计 shows a stacked bar per day (one month), month (one year) or year, with a legend that filters by kind, a table view and the works ranked for the range or the bar picked. Administrators also get every account's totals. Chart colours are new mode-level tokens (--color-chart-1..4), validated for colour-vision separation on both surfaces. The React UI review now covers the page and names the overflowing elements when a screen overflows. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
17 lines
755 B
TypeScript
17 lines
755 B
TypeScript
// @vitest-environment jsdom
|
|
import { expect, it, vi } from 'vitest';
|
|
import { ReaderScreen } from '../src/ui/reader-screen.tsx';
|
|
|
|
it('publishes asynchronous voice changes without reopening the existing settings panel', () => {
|
|
const context = {
|
|
voices: [], lastSpeechQueue: [], chrome: { tts: {} }, speechQueueIndex: () => 0,
|
|
patch: vi.fn(), setStatus: vi.fn(), view: null, readingTime: { setSpeaking: vi.fn() },
|
|
};
|
|
const voice = { id: 'zh', name: '中文', lang: 'zh-CN', default: true };
|
|
Reflect.apply(ReaderScreen.prototype['renderSpeechState'], context, [{
|
|
state: 'idle', voices: [voice], total: 0, index: -1, chunk: '', error: '',
|
|
}]);
|
|
expect(context.voices).toEqual([voice]);
|
|
expect(context.patch).toHaveBeenCalled();
|
|
});
|