Add frontend boot diagnostics
This commit is contained in:
@@ -53,6 +53,7 @@ export interface SpootyWorkspaceTabsState {
|
|||||||
const WORKSPACE_STORAGE_KEY = 'jarri_spooty_workspace_state_v1';
|
const WORKSPACE_STORAGE_KEY = 'jarri_spooty_workspace_state_v1';
|
||||||
const WORKSPACE_TABS_STORAGE_KEY = 'jarri_spooty_workspace_tabs_v1';
|
const WORKSPACE_TABS_STORAGE_KEY = 'jarri_spooty_workspace_tabs_v1';
|
||||||
const ARCHIVE_DESTINATION_KEY = 'jarri_spooty_archive_destination_v1';
|
const ARCHIVE_DESTINATION_KEY = 'jarri_spooty_archive_destination_v1';
|
||||||
|
const AUTH_TOKEN_STORAGE_KEY = 'spooty_auth_token';
|
||||||
const GRID_SIZE = 12;
|
const GRID_SIZE = 12;
|
||||||
const MIN_PANEL_WIDTH = 260;
|
const MIN_PANEL_WIDTH = 260;
|
||||||
const MIN_PANEL_HEIGHT = 160;
|
const MIN_PANEL_HEIGHT = 160;
|
||||||
@@ -176,9 +177,26 @@ export function defaultWorkspaceTabsState(): SpootyWorkspaceTabsState {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getStorageItem(key: string): string | null {
|
||||||
|
try {
|
||||||
|
return localStorage.getItem(key);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Failed to read localStorage key "${key}"`, error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function setStorageItem(key: string, value: string): void {
|
||||||
|
try {
|
||||||
|
localStorage.setItem(key, value);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Failed to write localStorage key "${key}"`, error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export function loadWorkspaceState(): SpootyWorkspaceState {
|
export function loadWorkspaceState(): SpootyWorkspaceState {
|
||||||
try {
|
try {
|
||||||
const raw = localStorage.getItem(WORKSPACE_STORAGE_KEY);
|
const raw = getStorageItem(WORKSPACE_STORAGE_KEY);
|
||||||
|
|
||||||
if (!raw) {
|
if (!raw) {
|
||||||
return defaultWorkspaceState();
|
return defaultWorkspaceState();
|
||||||
@@ -204,7 +222,7 @@ export function loadWorkspaceState(): SpootyWorkspaceState {
|
|||||||
|
|
||||||
export function loadWorkspaceTabsState(): SpootyWorkspaceTabsState {
|
export function loadWorkspaceTabsState(): SpootyWorkspaceTabsState {
|
||||||
try {
|
try {
|
||||||
const raw = localStorage.getItem(WORKSPACE_TABS_STORAGE_KEY);
|
const raw = getStorageItem(WORKSPACE_TABS_STORAGE_KEY);
|
||||||
|
|
||||||
if (raw) {
|
if (raw) {
|
||||||
const parsed = JSON.parse(raw) as SpootyWorkspaceTabsState;
|
const parsed = JSON.parse(raw) as SpootyWorkspaceTabsState;
|
||||||
@@ -318,8 +336,8 @@ export class AppComponent implements OnDestroy {
|
|||||||
selectedTrack$ = this.trackService.selectedTrack$;
|
selectedTrack$ = this.trackService.selectedTrack$;
|
||||||
version = this.versionService.getVersion();
|
version = this.versionService.getVersion();
|
||||||
spotifyConnected = false;
|
spotifyConnected = false;
|
||||||
authTokenPresent = Boolean(localStorage.getItem('spooty_auth_token'));
|
authTokenPresent = Boolean(getStorageItem(AUTH_TOKEN_STORAGE_KEY));
|
||||||
archiveDestination = localStorage.getItem(ARCHIVE_DESTINATION_KEY) || '';
|
archiveDestination = getStorageItem(ARCHIVE_DESTINATION_KEY) || '';
|
||||||
archiveListing?: ArchiveListing;
|
archiveListing?: ArchiveListing;
|
||||||
archiveLoading = false;
|
archiveLoading = false;
|
||||||
archiveError = '';
|
archiveError = '';
|
||||||
@@ -370,7 +388,7 @@ export class AppComponent implements OnDestroy {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
localStorage.setItem('spooty_auth_token', token);
|
setStorageItem(AUTH_TOKEN_STORAGE_KEY, token);
|
||||||
this.authTokenPresent = true;
|
this.authTokenPresent = true;
|
||||||
url.searchParams.delete('token');
|
url.searchParams.delete('token');
|
||||||
window.history.replaceState({}, document.title, url.toString());
|
window.history.replaceState({}, document.title, url.toString());
|
||||||
@@ -411,7 +429,7 @@ export class AppComponent implements OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
saveArchiveDestination(): void {
|
saveArchiveDestination(): void {
|
||||||
localStorage.setItem(ARCHIVE_DESTINATION_KEY, this.archiveDestination);
|
setStorageItem(ARCHIVE_DESTINATION_KEY, this.archiveDestination);
|
||||||
}
|
}
|
||||||
|
|
||||||
refreshArchive(): void {
|
refreshArchive(): void {
|
||||||
@@ -769,7 +787,7 @@ export class AppComponent implements OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private saveWorkspaceState(): void {
|
private saveWorkspaceState(): void {
|
||||||
localStorage.setItem(
|
setStorageItem(
|
||||||
WORKSPACE_TABS_STORAGE_KEY,
|
WORKSPACE_TABS_STORAGE_KEY,
|
||||||
JSON.stringify(this.workspaceTabsState),
|
JSON.stringify(this.workspaceTabsState),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -3,7 +3,13 @@ import { HttpInterceptorFn } from '@angular/common/http';
|
|||||||
const TOKEN_STORAGE_KEY = 'spooty_auth_token';
|
const TOKEN_STORAGE_KEY = 'spooty_auth_token';
|
||||||
|
|
||||||
export const authTokenInterceptor: HttpInterceptorFn = (req, next) => {
|
export const authTokenInterceptor: HttpInterceptorFn = (req, next) => {
|
||||||
const token = localStorage.getItem(TOKEN_STORAGE_KEY);
|
let token: string | null = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
token = localStorage.getItem(TOKEN_STORAGE_KEY);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to read auth token from localStorage', error);
|
||||||
|
}
|
||||||
|
|
||||||
if (!token) {
|
if (!token) {
|
||||||
return next(req);
|
return next(req);
|
||||||
|
|||||||
@@ -8,6 +8,21 @@
|
|||||||
<link rel="icon" type="image/x-icon" href="favicon.ico">
|
<link rel="icon" type="image/x-icon" href="favicon.ico">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
<div id="boot-status">Loading Jarri Spooty...</div>
|
||||||
<app-root></app-root>
|
<app-root></app-root>
|
||||||
|
<script>
|
||||||
|
window.addEventListener('error', function (event) {
|
||||||
|
var boot = document.getElementById('boot-status');
|
||||||
|
if (boot) {
|
||||||
|
boot.textContent = 'Frontend error: ' + (event.message || 'unknown');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
window.addEventListener('unhandledrejection', function (event) {
|
||||||
|
var boot = document.getElementById('boot-status');
|
||||||
|
if (boot) {
|
||||||
|
boot.textContent = 'Frontend rejection: ' + String(event.reason || 'unknown');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -5,6 +5,9 @@ import { AppComponent } from './app/app.component';
|
|||||||
import { devTools } from '@ngneat/elf-devtools';
|
import { devTools } from '@ngneat/elf-devtools';
|
||||||
|
|
||||||
bootstrapApplication(AppComponent, appConfig)
|
bootstrapApplication(AppComponent, appConfig)
|
||||||
|
.then(() => {
|
||||||
|
document.getElementById('boot-status')?.remove();
|
||||||
|
})
|
||||||
.catch((err) => console.error(err));
|
.catch((err) => console.error(err));
|
||||||
|
|
||||||
if (isDevMode()) {
|
if (isDevMode()) {
|
||||||
|
|||||||
Reference in New Issue
Block a user