openvibers/openvibe.games Commits
New worlds: the menu card says when each world was published (#368)
Changes
@@@ -78,8 +78,8 @@ describe('the menu network card (frame.menu-card@1)', () => {})expect(card.more).toEqual({ label: 'All worlds', url: 'https://openvibe.games/worlds' })expect(card.items).toEqual([-- { title: 'World a', meta: 'by Builder', url: 'https://openvibe.games/worlds/a' },-- { title: 'Moon base', meta: 'by Builder', url: 'https://openvibe.games/worlds/b' },++ { title: 'World a', meta: 'by Builder · just now', url: 'https://openvibe.games/worlds/a' },++ { title: 'Moon base', meta: 'by Builder · just now', url: 'https://openvibe.games/worlds/b' },])expect(card.title.length).toBeLessThanOrEqual(60)for (const item of card.items) {@@@ -93,6 +93,13 @@ describe('the menu network card (frame.menu-card@1)', () => {expect(contracts.validate('frame.menu-cards@1', [card]).valid).toBe(true)})++ it('says when each world was published, in the same short shape as the other cards', () => {++ expect(cardFrom([world('a', { publishedAt: NOW - 2 * 86_400_000 })], NOW).items[0]!.meta).toBe('by Builder · 2d ago')++ expect(cardFrom([world('a', { publishedAt: NOW - 3 * 3_600_000 })], NOW).items[0]!.meta).toBe('by Builder · 3h ago')++ // An old row with no published stamp still says when it was made rather than nothing.++ expect(cardFrom([world('a', { publishedAt: null, createdAt: NOW - 5 * 86_400_000 })], NOW).items[0]!.meta).toBe('by Builder · 5d ago')++ })++it('sends at most six, and never a draft or a hidden world', () => {const many = Array.from({ length: 8 }, (_, i) => world(`w${i}`))expect(cardFrom(many, NOW).items).toHaveLength(6)@@@ -29,16 +29,31 @@ export function plain(value: unknown, max: number): string {.slice(0, max)}--/** The card itself, newest published world first (the store's order). */++/** A short age for the card's one meta line ("2d ago"), the same shape the other sites' cards print. */++function shortAgo(ms: number, now: number): string {++ const min = Math.max(0, Math.round((now - ms) / 60000))++ if (min < 1) return 'just now'++ if (min < 60) return `${min}m ago`++ const h = Math.round(min / 60)++ if (h < 24) return `${h}h ago`++ return `${Math.round(h / 24)}d ago`++}++++/** The card itself, newest published world first (the store's order), each saying when it was published. */export function cardFrom(worlds: CreatorWorld[], at = Date.now()): MenuCard {const items = (Array.isArray(worlds) ? worlds : []).filter((w) => w && w.id && w.status === 'published').slice(0, 6)-- .map((w) => ({-- title: plain(w.title, 80),-- meta: `by ${plain(w.ownerName, 110)}`,-- url: `${BASE}/worlds/${encodeURIComponent(w.id)}`,-- }))++ .map((w) => {++ // A published world's own time; an old row with no published stamp falls back to when it was made.++ const published = w.publishedAt ?? w.createdAt++ const when = Number.isFinite(published) ? ` · ${shortAgo(published, at)}` : ''++ return {++ title: plain(w.title, 80),++ meta: `by ${plain(w.ownerName, 100)}${when}`,++ url: `${BASE}/worlds/${encodeURIComponent(w.id)}`,++ }++ })return menuCard({id: CARD_ID,site: SITE,
@@ -78,8 +78,8 @@ describe('the menu network card (frame.menu-card@1)', () => {
@@ -78,8 +78,8 @@ describe('the menu network card (frame.menu-card@1)', () => {
})
})
expect(card.more).toEqual({ label: 'All worlds', url: 'https://openvibe.games/worlds' })
expect(card.more).toEqual({ label: 'All worlds', url: 'https://openvibe.games/worlds' })
expect(card.items).toEqual([
expect(card.items).toEqual([
- { title: 'World a', meta: 'by Builder', url: 'https://openvibe.games/worlds/a' },
- { title: 'Moon base', meta: 'by Builder', url: 'https://openvibe.games/worlds/b' },
+ { title: 'World a', meta: 'by Builder · just now', url: 'https://openvibe.games/worlds/a' },
+ { title: 'Moon base', meta: 'by Builder · just now', url: 'https://openvibe.games/worlds/b' },
])
])
expect(card.title.length).toBeLessThanOrEqual(60)
expect(card.title.length).toBeLessThanOrEqual(60)
for (const item of card.items) {
for (const item of card.items) {
@@ -93,6 +93,13 @@ describe('the menu network card (frame.menu-card@1)', () => {
@@ -93,6 +93,13 @@ describe('the menu network card (frame.menu-card@1)', () => {
expect(contracts.validate('frame.menu-cards@1', [card]).valid).toBe(true)
expect(contracts.validate('frame.menu-cards@1', [card]).valid).toBe(true)
})
})
+ it('says when each world was published, in the same short shape as the other cards', () => {
+ expect(cardFrom([world('a', { publishedAt: NOW - 2 * 86_400_000 })], NOW).items[0]!.meta).toBe('by Builder · 2d ago')
+ expect(cardFrom([world('a', { publishedAt: NOW - 3 * 3_600_000 })], NOW).items[0]!.meta).toBe('by Builder · 3h ago')
+ // An old row with no published stamp still says when it was made rather than nothing.
+ expect(cardFrom([world('a', { publishedAt: null, createdAt: NOW - 5 * 86_400_000 })], NOW).items[0]!.meta).toBe('by Builder · 5d ago')
+ })
+
it('sends at most six, and never a draft or a hidden world', () => {
it('sends at most six, and never a draft or a hidden world', () => {
const many = Array.from({ length: 8 }, (_, i) => world(`w${i}`))
const many = Array.from({ length: 8 }, (_, i) => world(`w${i}`))
expect(cardFrom(many, NOW).items).toHaveLength(6)
expect(cardFrom(many, NOW).items).toHaveLength(6)
@@ -29,16 +29,31 @@ export function plain(value: unknown, max: number): string {
@@ -29,16 +29,31 @@ export function plain(value: unknown, max: number): string {
.slice(0, max)
.slice(0, max)
}
}
-/** The card itself, newest published world first (the store's order). */
+/** A short age for the card's one meta line ("2d ago"), the same shape the other sites' cards print. */
+function shortAgo(ms: number, now: number): string {
+ const min = Math.max(0, Math.round((now - ms) / 60000))
+ if (min < 1) return 'just now'
+ if (min < 60) return `${min}m ago`
+ const h = Math.round(min / 60)
+ if (h < 24) return `${h}h ago`
+ return `${Math.round(h / 24)}d ago`
+}
+
+/** The card itself, newest published world first (the store's order), each saying when it was published. */
export function cardFrom(worlds: CreatorWorld[], at = Date.now()): MenuCard {
export function cardFrom(worlds: CreatorWorld[], at = Date.now()): MenuCard {
const items = (Array.isArray(worlds) ? worlds : [])
const items = (Array.isArray(worlds) ? worlds : [])
.filter((w) => w && w.id && w.status === 'published')
.filter((w) => w && w.id && w.status === 'published')
.slice(0, 6)
.slice(0, 6)
- .map((w) => ({
- title: plain(w.title, 80),
- meta: `by ${plain(w.ownerName, 110)}`,
- url: `${BASE}/worlds/${encodeURIComponent(w.id)}`,
- }))
+ .map((w) => {
+ // A published world's own time; an old row with no published stamp falls back to when it was made.
+ const published = w.publishedAt ?? w.createdAt
+ const when = Number.isFinite(published) ? ` · ${shortAgo(published, at)}` : ''
+ return {
+ title: plain(w.title, 80),
+ meta: `by ${plain(w.ownerName, 100)}${when}`,
+ url: `${BASE}/worlds/${encodeURIComponent(w.id)}`,
+ }
+ })
return menuCard({
return menuCard({
id: CARD_ID,
id: CARD_ID,
site: SITE,
site: SITE,