|
1 | 1 | import { Suspense } from 'react' |
2 | | - |
3 | 2 | import type { Metadata } from 'next' |
| 3 | + |
4 | 4 | import PersonsClient from '../../../src/components/Persons/PersonsClient' |
5 | | -import getAlbum from '../../../src/lib/album' |
6 | | -import getAlbums from '../../../src/lib/albums' |
7 | 5 | import getGalleries from '../../../src/lib/galleries' |
8 | | -import indexKeywords, { addGeographyToSearch } from '../../../src/lib/search' |
9 | | -import config from '../../../src/models/config' |
10 | | -import type { AlbumMeta, Gallery, Item, ServerSideAllItem } from '../../../src/types/common' |
11 | | -import type { All } from '../../../src/types/pages' |
| 6 | +import { getPersonsData } from '../../../src/lib/persons' |
| 7 | +import type { Gallery } from '../../../src/types/common' |
| 8 | +import { generateClusters } from '../../../src/lib/generate-clusters' |
| 9 | +import type { Item } from '../../../src/types/common' |
| 10 | + |
| 11 | +type AgeSummary = { |
| 12 | + ages: { age: number; count: number }[]; |
| 13 | +}; |
| 14 | + |
| 15 | +function buildAgeSummary(items: Item[]): AgeSummary { |
| 16 | + const counts = new Map<number, number>() |
| 17 | + items.forEach(it => { |
| 18 | + if (!it.persons || !it.filename) return |
| 19 | + const filenameDate = Array.isArray(it.filename) |
| 20 | + ? (it.filename[0] ?? '').substring(0, 10) |
| 21 | + : String(it.filename).substring(0, 10) |
| 22 | + const photoDate = (it as any).photoDate || filenameDate |
| 23 | + it.persons.forEach(p => { |
| 24 | + if (!p.dob) return |
| 25 | + const birth = new Date(p.dob.substring(0, 10)) |
| 26 | + const shot = new Date(photoDate.substring(0, 10)) |
| 27 | + if (Number.isNaN(birth.getTime()) || Number.isNaN(shot.getTime())) return |
| 28 | + let age = shot.getFullYear() - birth.getFullYear() |
| 29 | + const m = shot.getMonth() - birth.getMonth() |
| 30 | + if (m < 0 || (m === 0 && shot.getDate() < birth.getDate())) age -= 1 |
| 31 | + if (age >= 0) counts.set(age, (counts.get(age) || 0) + 1) |
| 32 | + }) |
| 33 | + }) |
| 34 | + return { |
| 35 | + ages: Array.from(counts.entries()) |
| 36 | + .map(([age, count]) => ({ age, count })) |
| 37 | + .sort((a, b) => a.age - b.age), |
| 38 | + } |
| 39 | +} |
12 | 40 |
|
13 | 41 | export const metadata: Metadata = { |
14 | 42 | title: 'Persons - History App', |
15 | 43 | } |
16 | 44 |
|
17 | 45 | export async function generateStaticParams() { |
18 | 46 | const { galleries } = await getGalleries() |
19 | | - |
20 | | - return galleries.map((gallery) => ({ |
21 | | - gallery, |
22 | | - })) |
23 | | -} |
24 | | - |
25 | | -async function getPersonsData({ gallery }: All.Params): Promise<All.ComponentProps> { |
26 | | - const { [gallery]: { albums } } = await getAlbums(gallery) |
27 | | - |
28 | | - const prepareItems = ( |
29 | | - { albumName, albumCoordinateAccuracy, items }: |
30 | | - { |
31 | | - albumName: AlbumMeta['albumName'], |
32 | | - albumCoordinateAccuracy: NonNullable<AlbumMeta['geo']>['zoom'], |
33 | | - items: Item[], |
34 | | - }, |
35 | | - ) => items.map((item) => ({ |
36 | | - ...item, |
37 | | - gallery, |
38 | | - album: albumName, |
39 | | - corpus: [item.description, item.caption, item.location, item.city, item.search].join(' '), |
40 | | - coordinateAccuracy: item.coordinateAccuracy ?? albumCoordinateAccuracy, |
41 | | - search: addGeographyToSearch(item), |
42 | | - })) |
43 | | - |
44 | | - // reverse order for albums in ascending order (oldest on top) |
45 | | - const allItems = (await albums.reduce(async (previousPromise, album) => { |
46 | | - const prev = await previousPromise |
47 | | - const { album: { items, meta } } = await getAlbum(gallery, album.name) |
48 | | - const albumCoordinateAccuracy = meta?.geo?.zoom ?? config.defaultZoom |
49 | | - const preparedItems = prepareItems({ |
50 | | - albumName: album.name, |
51 | | - albumCoordinateAccuracy, |
52 | | - items, |
53 | | - }) |
54 | | - return prev.concat(preparedItems) |
55 | | - }, Promise.resolve([] as ServerSideAllItem[]))).reverse() |
56 | | - |
57 | | - return { |
58 | | - items: allItems, ...indexKeywords(allItems), |
59 | | - } |
| 47 | + return galleries.map((gallery) => ({ gallery })) |
60 | 48 | } |
61 | 49 |
|
62 | 50 | export default async function PersonsServer(props: { params: Promise<{ gallery: Gallery }> }) { |
63 | 51 | const params = await props.params |
64 | | - |
65 | | - const { |
66 | | - gallery, |
67 | | - } = params |
| 52 | + const { gallery } = params |
68 | 53 |
|
69 | 54 | const { items, indexedKeywords } = await getPersonsData({ gallery }) |
| 55 | + const clusterMarkers = generateClusters(items) |
| 56 | + const initialAgeSummary = buildAgeSummary(items) |
| 57 | + |
70 | 58 | return ( |
71 | | - <Suspense fallback={<div>Loading...</div>}> |
72 | | - <PersonsClient items={items} indexedKeywords={indexedKeywords} /> |
| 59 | + <Suspense fallback={<div>Loading Persons...</div>}> |
| 60 | + <PersonsClient |
| 61 | + items={items} |
| 62 | + indexedKeywords={indexedKeywords} |
| 63 | + clusteredMarkers={clusterMarkers} |
| 64 | + initialAgeSummary={initialAgeSummary} |
| 65 | + /> |
73 | 66 | </Suspense> |
74 | 67 | ) |
75 | 68 | } |
0 commit comments