Render data and uploader name for current photo in gallery
CI / Lint (pull_request) Successful in 1m0s
CI / Test (pull_request) Successful in 1m12s
Release Drafter / Update release notes draft (pull_request) Successful in 5s

This commit is contained in:
2026-08-19 18:05:52 -06:00
parent 0f0874a07a
commit bfcf855020
8 changed files with 368 additions and 39 deletions
+5
View File
@@ -36,11 +36,16 @@ export class MockNostrDataService extends Service {
@tracked mailboxes = null;
@tracked blossomServers = [];
@tracked placePhotos = [];
@tracked profiles = {};
store = {
add: () => {},
};
getProfile(pubkey) {
return this.profiles[pubkey];
}
get activeReadRelays() {
return [];
}
@@ -339,4 +339,143 @@ module('Integration | Component | photo-gallery', function (hooks) {
await triggerKeyEvent(document, 'keydown', 'Escape');
assert.ok(closed, 'gallery was closed on escape key');
});
test('it renders uploader name and date when profile is loaded', async function (assert) {
const displayName = 'Alice';
const publishedAt = Math.floor(Date.now() / 1000) - 60 * 60 * 24; // 1 day ago
this.nostrData.profiles = {
[USER_A]: { displayName, display_name: 'ignored', name: 'ignored' },
};
this.photos = [
{
eventId: 'event1',
pubkey: USER_A,
placeIdentifier: 'osm:node:12345',
url: 'https://example.com/photo.jpg',
publishedAt,
createdAt: publishedAt + 10,
},
];
this.selectedPhoto = this.photos[0];
await render(
<template>
<div id="test-container">
<div id="modal-portal"></div>
<PhotoGallery
@photos={{this.photos}}
@selectedPhoto={{this.selectedPhoto}}
/>
</div>
</template>
);
assert
.dom('.photo-gallery-uploader-name')
.hasText(displayName, 'uploader name is rendered');
assert
.dom('.photo-gallery-uploader-date')
.exists('date element is rendered');
});
test('it prefers published_at over created_at for the date', async function (assert) {
this.nostrData.profiles = {
[USER_A]: { displayName: 'Alice' },
};
const publishedAt = Math.floor(Date.now() / 1000) - 60 * 60 * 24 * 3; // 3 days ago
const createdAt = Math.floor(Date.now() / 1000) - 60; // 1 min ago
this.photos = [
{
eventId: 'event1',
pubkey: USER_A,
url: 'https://example.com/photo.jpg',
publishedAt,
createdAt,
},
];
this.selectedPhoto = this.photos[0];
await render(
<template>
<div id="test-container">
<div id="modal-portal"></div>
<PhotoGallery
@photos={{this.photos}}
@selectedPhoto={{this.selectedPhoto}}
/>
</div>
</template>
);
// Should show "3 days ago" (publishedAt), not "just now" (createdAt)
assert.dom('.photo-gallery-uploader-date').hasText('3 days ago');
});
test('it does not render uploader info when profile is missing', async function (assert) {
this.nostrData.profiles = {};
this.photos = [
{
eventId: 'event1',
pubkey: USER_A,
url: 'https://example.com/photo.jpg',
createdAt: Math.floor(Date.now() / 1000),
},
];
this.selectedPhoto = this.photos[0];
await render(
<template>
<div id="test-container">
<div id="modal-portal"></div>
<PhotoGallery
@photos={{this.photos}}
@selectedPhoto={{this.selectedPhoto}}
/>
</div>
</template>
);
assert
.dom('.photo-gallery-uploader-name')
.doesNotExist('uploader name is not rendered without profile');
// Date should still render since createdAt is present
assert.dom('.photo-gallery-uploader-date').exists('date is still rendered');
});
test('it falls back through displayName -> display_name -> name', async function (assert) {
this.nostrData.profiles = {
[USER_A]: { display_name: 'Bob', name: 'Robert' },
};
this.photos = [
{
eventId: 'event1',
pubkey: USER_A,
url: 'https://example.com/photo.jpg',
createdAt: Math.floor(Date.now() / 1000) - 60,
},
];
this.selectedPhoto = this.photos[0];
await render(
<template>
<div id="test-container">
<div id="modal-portal"></div>
<PhotoGallery
@photos={{this.photos}}
@selectedPhoto={{this.selectedPhoto}}
/>
</div>
</template>
);
assert
.dom('.photo-gallery-uploader-name')
.hasText('Bob', 'falls back to display_name when displayName is missing');
});
});
+60
View File
@@ -210,6 +210,66 @@ module('Unit | Utility | nostr', function () {
assert.strictEqual(photos[0].alt, null);
});
test('parsePlacePhotos extracts published_at when present', function (assert) {
const events = [
{
id: 'event-1',
pubkey: 'pubkey-1',
created_at: 200,
tags: [
['i', 'osm:node:123'],
['published_at', '100'],
['imeta', 'url https://example.com/photo.jpg', 'dim 800x600'],
],
},
];
const photos = parsePlacePhotos(events);
assert.strictEqual(photos.length, 1);
assert.strictEqual(photos[0].publishedAt, 100);
assert.strictEqual(photos[0].createdAt, 200);
});
test('parsePlacePhotos sets publishedAt to null when not present', function (assert) {
const events = [
{
id: 'event-1',
pubkey: 'pubkey-1',
created_at: 200,
tags: [
['i', 'osm:node:123'],
['imeta', 'url https://example.com/photo.jpg', 'dim 800x600'],
],
},
];
const photos = parsePlacePhotos(events);
assert.strictEqual(photos.length, 1);
assert.strictEqual(photos[0].publishedAt, null);
});
test('parsePlacePhotos ignores invalid published_at values', function (assert) {
const events = [
{
id: 'event-1',
pubkey: 'pubkey-1',
created_at: 200,
tags: [
['i', 'osm:node:123'],
['published_at', 'not-a-number'],
['imeta', 'url https://example.com/photo.jpg', 'dim 800x600'],
],
},
];
const photos = parsePlacePhotos(events);
assert.strictEqual(photos.length, 1);
assert.strictEqual(photos[0].publishedAt, null);
});
test('uniqNormalizedRelays returns normalized unique relays', function (assert) {
const relays = uniqNormalizedRelays([
'Relay.example.com',