Add alt text to photo uploads
CI / Lint (pull_request) Successful in 53s
CI / Test (pull_request) Successful in 1m6s
Release Drafter / Update release notes draft (pull_request) Successful in 5s

- Form field for optionally adding a description
- Publish as `imeta` `alt`
- Render as `<img>` alt when set

Fixes a placeholder being published instead. Also strips the current
placeholder if set as alt text (for existing uploads).
This commit is contained in:
2026-08-07 07:25:53 -06:00
parent ac9870240f
commit 9807a2b822
7 changed files with 140 additions and 28 deletions
@@ -97,4 +97,27 @@ module('Integration | Component | place-photo-upload', function (hooks) {
assert.dom('.photo-tag-suggestions').doesNotExist();
});
test('it renders an optional alt text input after upload selection', async function (assert) {
this.place = {
title: 'Cafe Alpha',
osmId: '123',
osmType: 'node',
osmTags: { amenity: 'cafe' },
};
await render(
<template><PlacePhotoUpload @place={{this.place}} /></template>
);
assert.dom('#photo-alt-input').doesNotExist();
const file = new File(['test'], 'photo.jpg', { type: 'image/jpeg' });
await selectFile(this.element, file);
assert.dom('#photo-alt-input').exists();
assert
.dom('#photo-alt-input')
.hasAttribute('placeholder', 'Describe this photo');
});
});
+62
View File
@@ -148,6 +148,68 @@ module('Unit | Utility | nostr', function () {
assert.strictEqual(photos[1].placeIdentifier, 'osm:node:456');
});
test('parsePlacePhotos extracts alt text from imeta', function (assert) {
const events = [
{
id: 'event-1',
pubkey: 'pubkey-1',
created_at: 100,
tags: [
[
'imeta',
'url https://example.com/photo.jpg',
'dim 800x600',
'alt A sunny terrace overlooking the bay',
],
],
},
];
const photos = parsePlacePhotos(events);
assert.strictEqual(photos.length, 1);
assert.strictEqual(photos[0].alt, 'A sunny terrace overlooking the bay');
});
test('parsePlacePhotos strips the legacy placeholder alt text', function (assert) {
const events = [
{
id: 'event-1',
pubkey: 'pubkey-1',
created_at: 100,
tags: [
[
'imeta',
'url https://example.com/photo.jpg',
'dim 800x600',
'alt A photo of a place',
],
],
},
];
const photos = parsePlacePhotos(events);
assert.strictEqual(photos.length, 1);
assert.strictEqual(photos[0].alt, null);
});
test('parsePlacePhotos leaves alt null when not provided', function (assert) {
const events = [
{
id: 'event-1',
pubkey: 'pubkey-1',
created_at: 100,
tags: [['imeta', 'url https://example.com/photo.jpg', 'dim 800x600']],
},
];
const photos = parsePlacePhotos(events);
assert.strictEqual(photos.length, 1);
assert.strictEqual(photos[0].alt, null);
});
test('uniqNormalizedRelays returns normalized unique relays', function (assert) {
const relays = uniqNormalizedRelays([
'Relay.example.com',