fixes generated image download

This commit is contained in:
2026-07-08 23:18:07 -07:00
parent 57198cd671
commit a4093e86b1
6 changed files with 101 additions and 30 deletions
+4 -3
View File
@@ -616,10 +616,11 @@
<div>
<strong>{{ artifact.file_name }}</strong>
<div class="artifact-meta">{{ artifact.platform }} • {{ artifact.artifact_type }} • {{
artifact.sha256.slice(0, 12) }}…</div>
artifact.sha256.slice(0, 12) }}… • {{ formatFileSize(artifact.size) }}</div>
</div>
<button class="btn-icon btn-edit" (click)="downloadArtifact(artifact)" [disabled]="downloadingArtifactId === artifact.id">
{{ downloadingArtifactId === artifact.id ? 'Downloading…' : 'Download' }}
<button class="btn-icon btn-edit"
(click)="downloadArtifact(artifact)">
Download
</button>
</div>
} @empty {
+21 -20
View File
@@ -11,6 +11,7 @@ import { TeamMember } from '../interfaces/team-member';
import { CommunityHighlight } from '../interfaces/community-highlight';
import { Underwriter } from '../interfaces/underwriter';
import {
MobileBuildArtifact,
MobileBuildDefaults,
MobileBuildRequest,
ThemeStatus,
@@ -126,8 +127,6 @@ export class AdminComponent implements OnInit {
mobileBuildErrorMessage = '';
mobileBuildPreflightIssues: string[] = [];
mobileBuildBusy = false;
downloadingArtifactId: number | null = null;
// ── Theme JSON status (mobile build tab) ────────────────────
readonly themeJsonStatus$ = new BehaviorSubject<ThemeStatus | null>(null);
readonly themeJsonLoading$ = new BehaviorSubject<boolean>(false);
@@ -452,24 +451,26 @@ export class AdminComponent implements OnInit {
return `${getAppConfig().apiBaseUrl}${path}`;
}
async downloadArtifact(artifact: { id: number; file_name: string }): Promise<void> {
try {
this.downloadingArtifactId = artifact.id;
const blob = await firstValueFrom(this.mobileBuildService.downloadArtifact(artifact.id));
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.style.display = 'none';
a.href = url;
a.download = artifact.file_name;
document.body.appendChild(a);
a.click();
window.URL.revokeObjectURL(url);
document.body.removeChild(a);
} catch {
this.mobileBuildErrorMessage = 'Failed to download artifact.';
} finally {
this.downloadingArtifactId = null;
}
formatFileSize(bytes: number): string {
if (bytes === 0) return '0 B';
const units = ['B', 'KB', 'MB', 'GB'];
const i = Math.floor(Math.log(bytes) / Math.log(1024));
const size = bytes / Math.pow(1024, i);
return `${size.toFixed(size < 10 ? 1 : 0)} ${units[i]}`;
}
downloadArtifact(artifact: MobileBuildArtifact): void {
this.mobileBuildService
.getDownloadUrl(artifact.id)
.subscribe({
next: (res) => {
// Trigger native browser download with the signed URL
window.open(res.download_url, '_blank');
},
error: (err) => {
console.error('Failed to get download URL:', err);
},
});
}
// ── Show CRUD ───────────────────────────────────────────
+4 -4
View File
@@ -81,10 +81,10 @@ export class MobileBuildService {
);
}
downloadArtifact(artifactId: number): Observable<Blob> {
return this.http.get(
`${this.baseUrl}/artifacts/${artifactId}/download`,
{ responseType: 'blob' },
getDownloadUrl(artifactId: number): Observable<{ download_url: string }> {
return this.http.get<{ download_url: string }>(
`${this.baseUrl}/artifacts/${artifactId}/download-url`,
);
}
}