diff --git a/apps/app-frontend/src/components/ui/RemoteAnnouncements.vue b/apps/app-frontend/src/components/ui/RemoteAnnouncements.vue index af06d37..a743cc7 100644 --- a/apps/app-frontend/src/components/ui/RemoteAnnouncements.vue +++ b/apps/app-frontend/src/components/ui/RemoteAnnouncements.vue @@ -236,6 +236,7 @@ async function refresh() { if (parsed && !disposed) { sync(parsed, true) saveCache(parsed) + for (const item of parsed) void loadImage(item, token) } } catch { // Remote fetch failed; previously cached items remain displayed @@ -316,6 +317,7 @@ onUnmounted(() => { if (interval) clearInterval(interval) if (advanceTimer) clearTimeout(advanceTimer) window.removeEventListener(OPEN_REMOTE_ANNOUNCEMENT_CENTER_EVENT, handleOpenCenter) + revokeImages() }) @@ -378,6 +380,12 @@ onUnmounted(() => { class="mt-1.5 size-2 shrink-0 rounded-full" :class="read.has(announcementKey(item)) ? 'opacity-0' : 'bg-red'" /> + {{ item.title @@ -393,6 +401,12 @@ onUnmounted(() => {

{{ selected.title }}

+
import { SpinnerIcon } from '@modrinth/assets' import { defineMessages, injectNotificationManager, useVIntl } from '@modrinth/ui' -import { computed, onMounted, ref } from 'vue' +import { computed, onMounted, onUnmounted, ref } from 'vue' import { type SkinSiteBugComment, skinSiteBugComments, + skinSiteBugImageFetch, type SkinSiteBugIssue, skinSiteBugIssue, type SkinSiteBugIssueType, @@ -169,6 +170,26 @@ function formatDateTime(ts: number): string { const canSubmit = computed(() => !commentSending.value && commentDraft.value.trim().length > 0) +const imageUrls = ref>({}) + +function revokeImageUrls() { + for (const url of Object.values(imageUrls.value)) URL.revokeObjectURL(url) + imageUrls.value = {} +} + +async function loadIssueImages(issue: SkinSiteBugIssue) { + revokeImageUrls() + const entries = await Promise.all( + (issue.images ?? []).map(async (image) => { + const blob = await skinSiteBugImageFetch(image.hash) + return blob ? [image.hash, URL.createObjectURL(blob)] : null + }), + ) + const map: Record = {} + for (const entry of entries) if (entry) map[entry[0]] = entry[1] + imageUrls.value = map +} + async function loadIssue() { loading.value = true failed.value = false @@ -176,6 +197,7 @@ async function loadIssue() { const result = await skinSiteBugIssue(props.issueId) if (!result.success) throw new Error(result.errorMessage) issue.value = result.payload + void loadIssueImages(result.payload) } catch (error) { failed.value = true handleError(error) @@ -241,6 +263,8 @@ onMounted(() => { void loadIssue() void loadComments(true) }) + +onUnmounted(revokeImageUrls)