Fix perpetual "loading" titles when first opening the contributions list
This commit is contained in:
@@ -6,8 +6,46 @@
|
|||||||
* succession). Entries are ordered newest-first.
|
* succession). Entries are ordered newest-first.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { tracked } from '@glimmer/tracking';
|
||||||
|
|
||||||
const HOUR_IN_SECONDS = 60 * 60;
|
const HOUR_IN_SECONDS = 60 * 60;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A single contribution timeline entry.
|
||||||
|
*
|
||||||
|
* `placeName` and `placeNameLoading` are tracked so that mutating them after
|
||||||
|
* the entry has been rendered (e.g. when the background OSM batch fetch
|
||||||
|
* resolves a place name) re-renders the consuming component. The remaining
|
||||||
|
* fields are static data and do not need to be tracked.
|
||||||
|
*/
|
||||||
|
export class ContributionEntry {
|
||||||
|
type = 'photo';
|
||||||
|
placeIdentifier;
|
||||||
|
osmType;
|
||||||
|
osmId;
|
||||||
|
photos;
|
||||||
|
createdAt;
|
||||||
|
eventCount;
|
||||||
|
@tracked placeName = null;
|
||||||
|
@tracked placeNameLoading = true;
|
||||||
|
|
||||||
|
constructor({
|
||||||
|
placeIdentifier,
|
||||||
|
osmType,
|
||||||
|
osmId,
|
||||||
|
photos,
|
||||||
|
createdAt,
|
||||||
|
eventCount,
|
||||||
|
}) {
|
||||||
|
this.placeIdentifier = placeIdentifier;
|
||||||
|
this.osmType = osmType;
|
||||||
|
this.osmId = osmId;
|
||||||
|
this.photos = photos;
|
||||||
|
this.createdAt = createdAt;
|
||||||
|
this.eventCount = eventCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses a single kind 360 (Place Photo) event's `imeta` tag into a photo object.
|
* Parses a single kind 360 (Place Photo) event's `imeta` tag into a photo object.
|
||||||
* Reuses the same field shape as `parsePlacePhotos` in `utils/nostr.js` but operates
|
* Reuses the same field shape as `parsePlacePhotos` in `utils/nostr.js` but operates
|
||||||
@@ -180,15 +218,12 @@ function buildEntry(placeIdentifier, events) {
|
|||||||
|
|
||||||
const [, osmType, osmId] = placeIdentifier.split(':');
|
const [, osmType, osmId] = placeIdentifier.split(':');
|
||||||
|
|
||||||
return {
|
return new ContributionEntry({
|
||||||
type: 'photo',
|
|
||||||
placeIdentifier,
|
placeIdentifier,
|
||||||
osmType,
|
osmType,
|
||||||
osmId,
|
osmId,
|
||||||
placeName: null,
|
|
||||||
placeNameLoading: true,
|
|
||||||
photos,
|
photos,
|
||||||
createdAt,
|
createdAt,
|
||||||
eventCount: events.length,
|
eventCount: events.length,
|
||||||
};
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
import { module, test } from 'qunit';
|
import { module, test } from 'qunit';
|
||||||
import { setupRenderingTest } from 'marco/tests/helpers';
|
import { setupRenderingTest } from 'marco/tests/helpers';
|
||||||
import { render, click } from '@ember/test-helpers';
|
import { render, click, settled } from '@ember/test-helpers';
|
||||||
import ContributionPhoto from 'marco/components/contribution-photo';
|
import ContributionPhoto from 'marco/components/contribution-photo';
|
||||||
|
import { ContributionEntry } from 'marco/utils/contributions';
|
||||||
|
|
||||||
function noop() {}
|
function noop() {}
|
||||||
|
|
||||||
@@ -145,4 +146,40 @@ module('Integration | Component | contribution-photo', function (hooks) {
|
|||||||
|
|
||||||
assert.strictEqual(selected, this.item);
|
assert.strictEqual(selected, this.item);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('it updates the place name when a tracked entry resolves after render', async function (assert) {
|
||||||
|
// Regression: when a place name is resolved by the background batch fetch
|
||||||
|
// after the entry has already been rendered as "Loading…", the component
|
||||||
|
// must re-render with the resolved name. This requires the entry's
|
||||||
|
// placeName/placeNameLoading to be tracked.
|
||||||
|
this.item = new ContributionEntry({
|
||||||
|
placeIdentifier: 'osm:node:12345',
|
||||||
|
osmType: 'node',
|
||||||
|
osmId: '12345',
|
||||||
|
createdAt: 1000,
|
||||||
|
photos: [
|
||||||
|
{
|
||||||
|
url: 'https://x.com/1.jpg',
|
||||||
|
thumbUrl: 'https://x.com/t1.jpg',
|
||||||
|
tags: [],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
await render(
|
||||||
|
<template>
|
||||||
|
<ContributionPhoto @item={{this.item}} @onSelect={{this.noop}} />
|
||||||
|
</template>
|
||||||
|
);
|
||||||
|
|
||||||
|
assert.dom('.contribution-name-loading').hasText('Loading…');
|
||||||
|
|
||||||
|
// Simulate the contributions service resolving the name after the batch fetch
|
||||||
|
this.item.placeName = 'Resolved Café';
|
||||||
|
this.item.placeNameLoading = false;
|
||||||
|
await settled();
|
||||||
|
|
||||||
|
assert.dom('.contribution-place').hasText('Resolved Café');
|
||||||
|
assert.dom('.contribution-name-loading').doesNotExist();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user