102 lines
2.4 KiB
TypeScript
102 lines
2.4 KiB
TypeScript
import { CommonModule } from '@angular/common';
|
|
import { AfterViewInit, Component, ElementRef, Input, OnChanges, OnDestroy, ViewChild } from '@angular/core';
|
|
|
|
import type { ChatEntry } from './models';
|
|
|
|
type JsonViewTree = object;
|
|
|
|
type JsonViewApi = {
|
|
renderJSON(value: unknown, container: HTMLElement): JsonViewTree;
|
|
expand?(tree: JsonViewTree): void;
|
|
destroy?(tree: JsonViewTree): void;
|
|
};
|
|
|
|
declare global {
|
|
interface Window {
|
|
jsonview?: JsonViewApi;
|
|
}
|
|
}
|
|
|
|
@Component({
|
|
selector: 'app-json-file-viewer',
|
|
imports: [CommonModule],
|
|
template: `
|
|
<div class="json-viewer-shell">
|
|
<div #host class="json-viewer-host"></div>
|
|
@if (errorMessage) {
|
|
<p class="json-viewer-error mb-0">{{ errorMessage }}</p>
|
|
}
|
|
</div>
|
|
`,
|
|
styleUrl: './json-file-viewer.component.scss',
|
|
})
|
|
export class JsonFileViewerComponent implements AfterViewInit, OnChanges, OnDestroy {
|
|
@Input({ required: true }) entry!: ChatEntry;
|
|
@ViewChild('host') private readonly host?: ElementRef<HTMLDivElement>;
|
|
|
|
errorMessage: string | null = null;
|
|
private renderedTree: JsonViewTree | null = null;
|
|
private renderVersion = 0;
|
|
|
|
ngAfterViewInit(): void {
|
|
void this.renderJsonTree();
|
|
}
|
|
|
|
ngOnChanges(): void {
|
|
void this.renderJsonTree();
|
|
}
|
|
|
|
ngOnDestroy(): void {
|
|
this.destroyRenderedTree();
|
|
}
|
|
|
|
private async renderJsonTree(): Promise<void> {
|
|
const host = this.host?.nativeElement;
|
|
|
|
if (!host || !this.entry.downloadUrl) {
|
|
return;
|
|
}
|
|
|
|
const renderVersion = ++this.renderVersion;
|
|
const jsonview = window.jsonview;
|
|
|
|
this.destroyRenderedTree();
|
|
host.replaceChildren();
|
|
this.errorMessage = null;
|
|
|
|
if (!jsonview) {
|
|
this.errorMessage = 'JSON viewer unavailable.';
|
|
return;
|
|
}
|
|
|
|
try {
|
|
const response = await fetch(this.entry.downloadUrl);
|
|
const content = await response.text();
|
|
const parsed = JSON.parse(content);
|
|
|
|
if (renderVersion !== this.renderVersion) {
|
|
return;
|
|
}
|
|
|
|
const tree = jsonview.renderJSON(parsed, host);
|
|
jsonview.expand?.(tree);
|
|
this.renderedTree = tree;
|
|
} catch {
|
|
if (renderVersion !== this.renderVersion) {
|
|
return;
|
|
}
|
|
|
|
this.errorMessage = 'Could not render JSON preview.';
|
|
}
|
|
}
|
|
|
|
private destroyRenderedTree(): void {
|
|
if (!this.renderedTree) {
|
|
return;
|
|
}
|
|
|
|
window.jsonview?.destroy?.(this.renderedTree);
|
|
this.renderedTree = null;
|
|
}
|
|
}
|