feat(attachments): downloadable attachments in ProvenancePanel + PostInfoHeader
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -21,6 +21,11 @@
|
||||
v-if="info.data.post.url" :href="info.data.post.url"
|
||||
target="_blank" rel="noopener noreferrer"
|
||||
>↗ original post</a>
|
||||
<a
|
||||
v-for="at in (info.data.attachments || [])" :key="at.id"
|
||||
class="fc-postinfo__attach"
|
||||
:href="at.download_url" :download="at.original_filename"
|
||||
>⬇ {{ at.original_filename }}</a>
|
||||
</div>
|
||||
</template>
|
||||
<span v-else-if="info && info.error" class="fc-postinfo__title">
|
||||
@@ -90,4 +95,8 @@ function clear() {
|
||||
font-size: 13px; color: rgb(var(--v-theme-on-surface-variant));
|
||||
}
|
||||
.fc-postinfo__main a { color: rgb(var(--v-theme-accent)); text-decoration: none; }
|
||||
.fc-postinfo__attach {
|
||||
font-size: 13px; color: rgb(var(--v-theme-accent));
|
||||
text-decoration: none;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -44,6 +44,17 @@
|
||||
/>
|
||||
</article>
|
||||
</template>
|
||||
|
||||
<div v-if="attachments.length" class="fc-prov__attach">
|
||||
<h4 class="fc-prov__attach-title">Attachments</h4>
|
||||
<a
|
||||
v-for="at in attachments" :key="at.id"
|
||||
class="fc-prov__attach-row"
|
||||
:href="at.download_url" :download="at.original_filename"
|
||||
>⬇ {{ at.original_filename }}
|
||||
<span class="fc-prov__attach-size">({{ at.size_bytes }} B)</span>
|
||||
</a>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
@@ -77,6 +88,8 @@ const show = computed(() => {
|
||||
return Array.isArray(st.entries) && st.entries.length > 0
|
||||
})
|
||||
|
||||
const attachments = computed(() => state.value?.attachments || [])
|
||||
|
||||
function postDate(e) { return formatPostDate(e.post.date) }
|
||||
|
||||
function openPost(postId) {
|
||||
@@ -124,4 +137,14 @@ function openPost(postId) {
|
||||
color: rgb(var(--v-theme-on-surface-variant));
|
||||
}
|
||||
.fc-prov__desc :deep(a) { color: rgb(var(--v-theme-accent)); }
|
||||
.fc-prov__attach { padding: 0 0 12px; }
|
||||
.fc-prov__attach-title {
|
||||
font-size: 13px; color: rgb(var(--v-theme-on-surface-variant));
|
||||
margin: 8px 0 4px;
|
||||
}
|
||||
.fc-prov__attach-row {
|
||||
display: block; font-size: 13px; text-decoration: none;
|
||||
color: rgb(var(--v-theme-accent)); padding: 2px 0;
|
||||
}
|
||||
.fc-prov__attach-size { color: rgb(var(--v-theme-on-surface-variant)); }
|
||||
</style>
|
||||
|
||||
@@ -16,8 +16,11 @@ export const useProvenanceStore = defineStore('provenance', () => {
|
||||
imageCache.value[id] = { loading: true, error: null, entries: null }
|
||||
try {
|
||||
const body = await api.get(`/api/provenance/image/${id}`)
|
||||
imageCache.value[id] =
|
||||
{ loading: false, error: null, entries: body.provenance }
|
||||
imageCache.value[id] = {
|
||||
loading: false, error: null,
|
||||
entries: body.provenance,
|
||||
attachments: body.attachments || [],
|
||||
}
|
||||
} catch (e) {
|
||||
imageCache.value[id] =
|
||||
{ loading: false, error: e.message, entries: null }
|
||||
|
||||
@@ -59,4 +59,18 @@ describe('provenance store', () => {
|
||||
expect(globalThis.fetch.mock.calls.length).toBe(1)
|
||||
expect(store.postInfo(3).data.artist.slug).toBe('a')
|
||||
})
|
||||
|
||||
it('loadForImage keeps attachments from the payload', async () => {
|
||||
const store = useProvenanceStore()
|
||||
stubFetch(() => ({
|
||||
status: 200,
|
||||
body: {
|
||||
image_id: 1, provenance: [],
|
||||
attachments: [{ id: 9, original_filename: 'p.zip', size_bytes: 5,
|
||||
ext: '.zip', download_url: '/api/attachments/9/download' }]
|
||||
}
|
||||
}))
|
||||
await store.loadForImage(1)
|
||||
expect(store.imageProv(1).attachments[0].id).toBe(9)
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user