mirror of
https://git.vectorsigma.ru/public/photoview.git
synced 2026-08-03 19:39:19 +00:00
301 lines
6.9 KiB
TypeScript
301 lines
6.9 KiB
TypeScript
import { gql, useLazyQuery } from '@apollo/client'
|
|
import React, { useEffect } from 'react'
|
|
import { useTranslation } from 'react-i18next'
|
|
import { Link } from 'react-router-dom'
|
|
import styled from 'styled-components'
|
|
import { authToken } from '../../../helpers/authentication'
|
|
import { isNil } from '../../../helpers/utils'
|
|
import { MediaType } from '../../../__generated__/globalTypes'
|
|
import { SidebarFacesOverlay } from '../../facesOverlay/FacesOverlay'
|
|
import {
|
|
ProtectedImage,
|
|
ProtectedVideo,
|
|
ProtectedVideoProps_Media,
|
|
} from '../../photoGallery/ProtectedMedia'
|
|
import { SidebarPhotoCover } from '../AlbumCovers'
|
|
import { SidebarPhotoShare } from '../Sharing'
|
|
import SidebarMediaDownload from '../SidebarDownloadMedia'
|
|
import SidebarHeader from '../SidebarHeader'
|
|
import { sidebarDownloadQuery_media_downloads } from '../__generated__/sidebarDownloadQuery'
|
|
import ExifDetails from './MediaSidebarExif'
|
|
import MediaSidebarPeople from './MediaSidebarPeople'
|
|
import MediaSidebarMap from './MediaSidebarMap'
|
|
import {
|
|
sidebarMediaQuery,
|
|
sidebarMediaQueryVariables,
|
|
sidebarMediaQuery_media_album_path,
|
|
sidebarMediaQuery_media_exif,
|
|
sidebarMediaQuery_media_faces,
|
|
sidebarMediaQuery_media_thumbnail,
|
|
sidebarMediaQuery_media_videoMetadata,
|
|
} from './__generated__/sidebarMediaQuery'
|
|
import { BreadcrumbList } from '../../album/AlbumTitle'
|
|
|
|
export const SIDEBAR_MEDIA_QUERY = gql`
|
|
query sidebarMediaQuery($id: ID!) {
|
|
media(id: $id) {
|
|
id
|
|
title
|
|
type
|
|
highRes {
|
|
url
|
|
width
|
|
height
|
|
}
|
|
thumbnail {
|
|
url
|
|
width
|
|
height
|
|
}
|
|
videoWeb {
|
|
url
|
|
width
|
|
height
|
|
}
|
|
videoMetadata {
|
|
id
|
|
width
|
|
height
|
|
duration
|
|
codec
|
|
framerate
|
|
bitrate
|
|
colorProfile
|
|
audio
|
|
}
|
|
exif {
|
|
id
|
|
camera
|
|
maker
|
|
lens
|
|
dateShot
|
|
exposure
|
|
aperture
|
|
iso
|
|
focalLength
|
|
flash
|
|
exposureProgram
|
|
coordinates {
|
|
latitude
|
|
longitude
|
|
}
|
|
}
|
|
album {
|
|
id
|
|
title
|
|
path {
|
|
id
|
|
title
|
|
}
|
|
}
|
|
faces {
|
|
id
|
|
rectangle {
|
|
minX
|
|
maxX
|
|
minY
|
|
maxY
|
|
}
|
|
faceGroup {
|
|
id
|
|
label
|
|
imageFaceCount
|
|
}
|
|
media {
|
|
id
|
|
title
|
|
thumbnail {
|
|
url
|
|
width
|
|
height
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
`
|
|
|
|
const PreviewImage = styled(ProtectedImage)`
|
|
position: absolute;
|
|
width: 100%;
|
|
height: 100%;
|
|
top: 0;
|
|
left: 0;
|
|
object-fit: contain;
|
|
`
|
|
|
|
const PreviewVideo = styled(ProtectedVideo)`
|
|
position: absolute;
|
|
width: 100%;
|
|
height: 100%;
|
|
top: 0;
|
|
left: 0;
|
|
`
|
|
|
|
interface PreviewMediaPropsMedia extends ProtectedVideoProps_Media {
|
|
type: MediaType
|
|
}
|
|
|
|
type PreviewMediaProps = {
|
|
media: PreviewMediaPropsMedia
|
|
previewImage?: {
|
|
url: string
|
|
}
|
|
}
|
|
|
|
const PreviewMedia = ({ media, previewImage }: PreviewMediaProps) => {
|
|
if (media.type === MediaType.Photo) {
|
|
return <PreviewImage src={previewImage?.url} />
|
|
}
|
|
|
|
if (media.type === MediaType.Video) {
|
|
return <PreviewVideo media={media} />
|
|
}
|
|
|
|
return <div>ERROR: Unknown media type: {media.type}</div>
|
|
}
|
|
|
|
type SidebarContentProps = {
|
|
media: MediaSidebarMedia
|
|
hidePreview?: boolean
|
|
}
|
|
|
|
const SidebarContent = ({ media, hidePreview }: SidebarContentProps) => {
|
|
const { t } = useTranslation()
|
|
let previewImage = null
|
|
if (media.highRes) previewImage = media.highRes
|
|
else if (media.thumbnail) previewImage = media.thumbnail
|
|
|
|
const imageAspect =
|
|
previewImage?.width && previewImage?.height
|
|
? previewImage.height / previewImage.width
|
|
: 3 / 2
|
|
|
|
let sidebarMap = null
|
|
const mediaCoordinates = media.exif?.coordinates
|
|
if (mediaCoordinates) {
|
|
sidebarMap = <MediaSidebarMap coordinates={mediaCoordinates} />
|
|
}
|
|
|
|
let albumPath = null
|
|
const mediaAlbum = media.album
|
|
if (!isNil(mediaAlbum)) {
|
|
const pathElms = [...(mediaAlbum.path ?? []), mediaAlbum].map(album => (
|
|
<li key={album.id} className="inline-block hover:underline">
|
|
<Link
|
|
className="text-blue-900 dark:text-blue-200 hover:underline"
|
|
to={`/album/${album.id}`}
|
|
>
|
|
{album.title}
|
|
</Link>
|
|
</li>
|
|
))
|
|
|
|
albumPath = (
|
|
<div className="mx-4 my-4">
|
|
<h2 className="uppercase text-xs text-gray-900 dark:text-gray-300 font-semibold">
|
|
{t('sidebar.media.album_path', 'Album path')}
|
|
</h2>
|
|
<BreadcrumbList hideLastArrow={true}>{pathElms}</BreadcrumbList>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
return (
|
|
<div>
|
|
<SidebarHeader title={media.title ?? 'Loading...'} />
|
|
<div className="lg:mx-4">
|
|
{!hidePreview && (
|
|
<div
|
|
className="w-full h-0 relative"
|
|
style={{ paddingTop: `${Math.min(imageAspect, 0.75) * 100}%` }}
|
|
>
|
|
<PreviewMedia
|
|
previewImage={previewImage || undefined}
|
|
media={media}
|
|
/>
|
|
<SidebarFacesOverlay media={media} />
|
|
</div>
|
|
)}
|
|
</div>
|
|
<ExifDetails media={media} />
|
|
{albumPath}
|
|
<MediaSidebarPeople media={media} />
|
|
{sidebarMap}
|
|
<SidebarMediaDownload media={media} />
|
|
<SidebarPhotoShare id={media.id} />
|
|
<div className="mt-8">
|
|
<SidebarPhotoCover cover_id={media.id} />
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
export interface MediaSidebarMedia {
|
|
__typename: 'Media'
|
|
id: string
|
|
title?: string
|
|
type: MediaType
|
|
highRes?: null | {
|
|
__typename: 'MediaURL'
|
|
url: string
|
|
width?: number
|
|
height?: number
|
|
}
|
|
thumbnail?: sidebarMediaQuery_media_thumbnail | null
|
|
videoWeb?: null | {
|
|
__typename: 'MediaURL'
|
|
url: string
|
|
width?: number
|
|
height?: number
|
|
}
|
|
videoMetadata?: sidebarMediaQuery_media_videoMetadata | null
|
|
exif?: sidebarMediaQuery_media_exif | null
|
|
faces?: sidebarMediaQuery_media_faces[]
|
|
downloads?: sidebarDownloadQuery_media_downloads[]
|
|
album?: {
|
|
__typename: 'Album'
|
|
id: string
|
|
title: string
|
|
path?: sidebarMediaQuery_media_album_path[]
|
|
}
|
|
}
|
|
|
|
type MediaSidebarType = {
|
|
media: MediaSidebarMedia
|
|
hidePreview?: boolean
|
|
}
|
|
|
|
const MediaSidebar = ({ media, hidePreview }: MediaSidebarType) => {
|
|
const [loadMedia, { loading, error, data }] = useLazyQuery<
|
|
sidebarMediaQuery,
|
|
sidebarMediaQueryVariables
|
|
>(SIDEBAR_MEDIA_QUERY)
|
|
|
|
useEffect(() => {
|
|
if (media != null && authToken()) {
|
|
loadMedia({
|
|
variables: {
|
|
id: media.id,
|
|
},
|
|
})
|
|
}
|
|
}, [media])
|
|
|
|
if (!media) return null
|
|
|
|
if (!authToken()) {
|
|
return <SidebarContent media={media} hidePreview={hidePreview} />
|
|
}
|
|
|
|
if (error) return <div>{error.message}</div>
|
|
|
|
if (loading || data == null) {
|
|
return <SidebarContent media={media} hidePreview={hidePreview} />
|
|
}
|
|
|
|
return <SidebarContent media={data.media} hidePreview={hidePreview} />
|
|
}
|
|
|
|
export default MediaSidebar
|