mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-14 05:06:16 -08:00
perf(workspace): share the startup inventory read and defer non-first-card IPC (J1)
Journey J1 / renderer.ipcCallsToFirstCard: 12 -> 7. - PlaylistsService.getAllPlaylists() shares one in-flight SQLite read between concurrent callers (the playlist effect and the XMLTV source reconciliation at startup). Settled reads are never reused and every SQLite write detaches the pending read. - getM3uFavoriteChannels() stops re-reading the write-once IndexedDB -> SQLite migration receipt once it has been seen. - StartupDeferralService holds the download list, app update status and the dashboard's recent items and favorites until one task after the render that reveals the routed content (5 s safety timeout). IPTVNATOR_DISABLE_STARTUP_DEFERRAL=1 is the kill switch. - reconcileEpgSources and the two distinct migration-flag reads stay on the critical path: the first is the #1548 revision fence, the second are different keys, not duplicates. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
76dd8c099e
commit
e5ee064407
24 files changed
+648
-23
No files matched your search
@@ -7,7 +7,11 @@ import {
|
||||
selectPlaylistsLoadingFlag,
|
||||
} from '@iptvnator/m3u-state';
|
||||
import { of, Subject } from 'rxjs';
|
||||
import { DatabaseService, PlaylistsService } from '@iptvnator/services';
|
||||
import {
|
||||
DatabaseService,
|
||||
PlaylistsService,
|
||||
StartupDeferralService,
|
||||
} from '@iptvnator/services';
|
||||
import {
|
||||
PlaybackPositionData,
|
||||
Playlist,
|
||||
@@ -270,6 +274,30 @@ describe('DashboardDataService', () => {
|
||||
expect(service.dashboardReady()).toBe(false);
|
||||
});
|
||||
|
||||
it('holds the page-entry reload until the first screen rendered', async () => {
|
||||
let openGate!: () => void;
|
||||
jest.spyOn(
|
||||
TestBed.inject(StartupDeferralService),
|
||||
'whenFirstContentRendered'
|
||||
).mockReturnValue(new Promise<void>((resolve) => (openGate = resolve)));
|
||||
const recent = jest
|
||||
.spyOn(service, 'reloadGlobalRecentItems')
|
||||
.mockResolvedValue(undefined);
|
||||
const favorites = jest
|
||||
.spyOn(service, 'reloadGlobalFavorites')
|
||||
.mockResolvedValue(undefined);
|
||||
|
||||
const reload = service.reloadForPageEntry();
|
||||
await Promise.resolve();
|
||||
expect(recent).not.toHaveBeenCalled();
|
||||
expect(favorites).not.toHaveBeenCalled();
|
||||
|
||||
openGate();
|
||||
await reload;
|
||||
expect(recent).toHaveBeenCalledTimes(1);
|
||||
expect(favorites).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('keeps dashboardReady false until xtream recently added finishes its first load', async () => {
|
||||
expect(service.dashboardReady()).toBe(false);
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ import {
|
||||
GlobalRecentlyAddedKind,
|
||||
PlaylistsService,
|
||||
RuntimeCapabilitiesService,
|
||||
StartupDeferralService,
|
||||
} from '@iptvnator/services';
|
||||
import {
|
||||
XTREAM_DATA_SOURCE,
|
||||
@@ -116,6 +117,7 @@ export class DashboardDataService {
|
||||
private readonly xtreamDataSource = inject(XTREAM_DATA_SOURCE);
|
||||
private readonly playlistsService = inject(PlaylistsService);
|
||||
private readonly runtime = inject(RuntimeCapabilitiesService);
|
||||
private readonly startupDeferral = inject(StartupDeferralService);
|
||||
private readonly ngZone = inject(NgZone);
|
||||
private readonly translate = inject(TranslateService);
|
||||
private readonly playbackPositions = inject(PORTAL_PLAYBACK_POSITIONS);
|
||||
@@ -499,6 +501,19 @@ export class DashboardDataService {
|
||||
|
||||
readonly quickRecent = computed(() => this.recentPlaylists().slice(0, 4));
|
||||
|
||||
/**
|
||||
* Page-entry reload of recent items and favorites. On launch it waits for
|
||||
* the first rendered screen (journey J1) and the rails keep their
|
||||
* skeletons meanwhile; later entries find the gate already open.
|
||||
*/
|
||||
async reloadForPageEntry(): Promise<void> {
|
||||
await this.startupDeferral.whenFirstContentRendered();
|
||||
await Promise.all([
|
||||
this.reloadGlobalRecentItems(),
|
||||
this.reloadGlobalFavorites(),
|
||||
]);
|
||||
}
|
||||
|
||||
async reloadGlobalRecentItems(): Promise<void> {
|
||||
if (!this.globalRecentLoaded()) {
|
||||
this.globalRecentLoadingState.set(true);
|
||||
|
||||
+1
-2
@@ -304,8 +304,7 @@ export class WorkspaceDashboardRailsComponent {
|
||||
// Re-entering the dashboard should pick up any DB-backed recent/favorite
|
||||
// changes made while viewing details, including newly backfilled
|
||||
// backdrops that do not change recency ordering.
|
||||
void this.data.reloadGlobalRecentItems();
|
||||
void this.data.reloadGlobalFavorites();
|
||||
void this.data.reloadForPageEntry();
|
||||
|
||||
this.liveEpg.connect(this.enabledLiveCards);
|
||||
|
||||
|
||||
Reference in new issue
Block a user