feat: introduce @photo-gallery/data package for centralized photo management

- Added a new package `@photo-gallery/data` to manage photo data and metadata.
- Updated various components to utilize the new photoLoader from the data package.
- Adjusted dependencies in `pnpm-lock.yaml` and `package.json` files to include the new package.
- Refactored imports across the application to streamline photo data access.

Signed-off-by: Innei <tukon479@gmail.com>
This commit is contained in:
Innei
2025-06-08 20:46:19 +08:00
parent 6641ce4404
commit 67a948ddeb
16 changed files with 75 additions and 19 deletions

View File

@@ -72,7 +72,6 @@ photo-gallery-site/
│ │ │ └── worker/ # 并发处理
│ │ ├── modules/ # 功能模块
│ │ ├── pages/ # 页面组件
│ │ └── data/ # 数据文件
│ ├── public/ # 静态资源
│ └── scripts/ # 构建脚本
├── packages/webgl-viewer/ # WebGL 图像查看器

View File

@@ -18,6 +18,7 @@
"start": "next start"
},
"dependencies": {
"@photo-gallery/data": "workspace:*",
"linkedom": "0.18.11",
"react": "19.1.0",
"react-dom": "19.1.0"

View File

@@ -1,11 +1,16 @@
import { photoLoader } from '@photo-gallery/data'
import type { PhotoManifest } from '@photo-gallery/data/types'
import { DOMParser } from 'linkedom'
import type { HTMLDocument } from 'linkedom/types/html/document'
import type { NextRequest } from 'next/server'
import { photoLoader } from '../../../../web/src/data/photos'
import type { PhotoManifest } from '../../../../web/src/types/photo'
import { getIndexHtml } from '../../constants'
type HtmlElement = ReturnType<typeof DOMParser.prototype.parseFromString>
type OnlyHTMLDocument = HtmlElement extends infer T
? T extends { [key: string]: any; head: any }
? T
: never
: never
export const runtime = 'edge'
export const GET = async (
request: NextRequest,
@@ -54,7 +59,7 @@ export const GET = async (
}
const createAndInsertOpenGraphMeta = (
document: HTMLDocument,
document: OnlyHTMLDocument,
photo: PhotoManifest,
request: NextRequest,
) => {
@@ -68,7 +73,7 @@ const createAndInsertOpenGraphMeta = (
}
for (const [property, content] of Object.entries(ogTags)) {
const ogMeta = document.createElement('meta')
const ogMeta = document.createElement('meta', {})
ogMeta.setAttribute('property', property)
ogMeta.setAttribute('content', content)
document.head.append(ogMeta as unknown as Node)
@@ -83,7 +88,7 @@ const createAndInsertOpenGraphMeta = (
}
for (const [name, content] of Object.entries(twitterTags)) {
const twitterMeta = document.createElement('meta')
const twitterMeta = document.createElement('meta', {})
twitterMeta.setAttribute('name', name)
twitterMeta.setAttribute('content', content)
document.head.append(twitterMeta as unknown as Node)

View File

@@ -1,8 +1,7 @@
import { photoLoader } from '@photo-gallery/data'
import { ImageResponse } from 'next/og'
import type { NextRequest } from 'next/server'
import { photoLoader } from '../../../../../web/src/data/photos'
export const runtime = 'edge'
export const GET = async (

View File

@@ -13,7 +13,7 @@
"incremental": true,
"module": "esnext",
"esModuleInterop": true,
"moduleResolution": "node",
"moduleResolution": "bundler",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
@@ -32,4 +32,4 @@
"exclude": [
"node_modules"
]
}
}

View File

@@ -22,6 +22,7 @@
"@aws-sdk/s3-request-presigner": "3.824.0",
"@essentials/request-timeout": "1.3.0",
"@headlessui/react": "2.2.4",
"@photo-gallery/data": "workspace:*",
"@photo-gallery/webgl-viewer": "workspace:*",
"@radix-ui/react-context-menu": "2.2.15",
"@radix-ui/react-dropdown-menu": "2.1.15",

View File

@@ -1,9 +1,9 @@
import { photoLoader } from '@photo-gallery/data'
import { atom, useAtom, useAtomValue } from 'jotai'
import { useCallback, useEffect, useMemo } from 'react'
import { useLocation, useNavigate } from 'react-router'
import { gallerySettingAtom } from '~/atoms/app'
import { photoLoader } from '~/data/photos'
const openAtom = atom(false)
const currentIndexAtom = atom(0)

View File

@@ -1,4 +1,5 @@
import { siteConfig } from '@config'
import { photoLoader } from '@photo-gallery/data'
import { repository } from '@pkg'
import { useAtom } from 'jotai'
import { useRef, useState } from 'react'
@@ -13,7 +14,6 @@ import {
DropdownMenuTrigger,
} from '~/components/ui/dropdown-menu'
import { Slider } from '~/components/ui/slider'
import { photoLoader } from '~/data/photos'
import { useMobile } from '~/hooks/useMobile'
const allTags = photoLoader.getAllTags()

View File

@@ -1,6 +1,6 @@
import { siteConfig } from '@config'
import { photoLoader } from '@photo-gallery/data'
import { photoLoader } from '~/data/photos'
import { clsxm } from '~/lib/cn'
import { ActionGroup } from './ActionGroup'

View File

@@ -1,7 +1,6 @@
import { photoLoader } from '@photo-gallery/data'
import { Blurhash } from 'react-blurhash'
import { photoLoader } from '~/data/photos'
export const Component = () => {
const photos = photoLoader.getPhotos()

View File

@@ -1,11 +1,11 @@
import { siteConfig } from '@config'
import { photoLoader } from '@photo-gallery/data'
import { useAtomValue, useSetAtom } from 'jotai'
import { useEffect, useRef } from 'react'
import { Outlet, useParams, useSearchParams } from 'react-router'
import { gallerySettingAtom } from '~/atoms/app'
import { ScrollArea } from '~/components/ui/scroll-areas/ScrollArea'
import { photoLoader } from '~/data/photos'
import { usePhotoViewer } from '~/hooks/usePhotoViewer'
import { MasonryRoot } from '~/modules/gallery/MasonryRoot'

View File

@@ -0,0 +1,13 @@
{
"name": "@photo-gallery/data",
"type": "module",
"private": true,
"exports": {
".": "./src/index.ts",
"./types": "./src/types.ts"
},
"dependencies": {
"exif-reader": "2.0.2",
"fuji-recipes": "1.0.2"
}
}

View File

@@ -1,5 +1,5 @@
import type { PhotoManifest } from '../types/photo'
import PhotosManifest from './photos-manifest.json'
import type { PhotoManifest } from './types'
class PhotoLoader {
private photos: PhotoManifest[] = []

View File

@@ -0,0 +1 @@
../../../apps/web/src/data/photos-manifest.json

View File

@@ -0,0 +1,23 @@
import type { Exif } from 'exif-reader'
import type getRecipe from 'fuji-recipes'
export interface PhotoManifest {
id: string
title: string
description: string
views: number
tags: string[]
originalUrl: string
thumbnailUrl: string
blurhash: string
width: number
height: number
aspectRatio: number
s3Key: string
lastModified: string
size: number
exif: Exif & { FujiRecipe?: ReturnType<typeof getRecipe> }
isLivePhoto?: boolean
livePhotoVideoUrl?: string
livePhotoVideoS3Key?: string
}

17
pnpm-lock.yaml generated
View File

@@ -69,6 +69,9 @@ importers:
apps/ssr:
dependencies:
'@photo-gallery/data':
specifier: workspace:*
version: link:../../packages/data
linkedom:
specifier: 0.18.11
version: 0.18.11
@@ -109,6 +112,9 @@ importers:
'@headlessui/react':
specifier: 2.2.4
version: 2.2.4(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
'@photo-gallery/data':
specifier: workspace:*
version: link:../../packages/data
'@photo-gallery/webgl-viewer':
specifier: workspace:*
version: link:../../packages/webgl-viewer
@@ -318,6 +324,15 @@ importers:
specifier: 1.0.0-alpha.0
version: 1.0.0-alpha.0
packages/data:
dependencies:
exif-reader:
specifier: 2.0.2
version: 2.0.2
fuji-recipes:
specifier: 1.0.2
version: 1.0.2
packages/webgl-viewer:
dependencies:
react:
@@ -1588,7 +1603,7 @@ packages:
resolution: {integrity: sha512-fyjAACV62oPV925xFCrH8DR5xWhg9KYtJT4s3u54jxp+L/hbpTY2kIeEFFbFe+a/HCE94zGQMZLIpVTPVZDhaA==}
peerDependencies:
'@types/react': '*'
react: 18.3.1
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
peerDependenciesMeta:
'@types/react':
optional: true