mirror of
https://git.vectorsigma.ru/public/photoview.git
synced 2026-08-03 21:09:05 +00:00
Rewrite sidebar to Typescript
This commit is contained in:
@@ -17,6 +17,7 @@
|
|||||||
"add": "Tilføj",
|
"add": "Tilføj",
|
||||||
"cancel": "Annuller",
|
"cancel": "Annuller",
|
||||||
"delete": "Slet",
|
"delete": "Slet",
|
||||||
|
"more": "Mere",
|
||||||
"remove": "Fjern",
|
"remove": "Fjern",
|
||||||
"save": "Gem"
|
"save": "Gem"
|
||||||
},
|
},
|
||||||
@@ -76,7 +77,7 @@
|
|||||||
"title": "Billeder"
|
"title": "Billeder"
|
||||||
},
|
},
|
||||||
"places_page": {
|
"places_page": {
|
||||||
"title": null
|
"title": "Kort"
|
||||||
},
|
},
|
||||||
"routes": {
|
"routes": {
|
||||||
"page_not_found": "Side ikke fundet"
|
"page_not_found": "Side ikke fundet"
|
||||||
@@ -169,7 +170,7 @@
|
|||||||
},
|
},
|
||||||
"sidebar": {
|
"sidebar": {
|
||||||
"album": {
|
"album": {
|
||||||
"title": "Album indstillinger"
|
"title": "Indstillinger for album"
|
||||||
},
|
},
|
||||||
"download": {
|
"download": {
|
||||||
"filesize": {
|
"filesize": {
|
||||||
@@ -223,7 +224,7 @@
|
|||||||
"flash": "Blitz",
|
"flash": "Blitz",
|
||||||
"focal_length": "Brændvidde",
|
"focal_length": "Brændvidde",
|
||||||
"iso": "ISO",
|
"iso": "ISO",
|
||||||
"lens": "Lense",
|
"lens": "Objektiv",
|
||||||
"maker": "Mærke"
|
"maker": "Mærke"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
"add": "Add",
|
"add": "Add",
|
||||||
"cancel": "Cancel",
|
"cancel": "Cancel",
|
||||||
"delete": "Delete",
|
"delete": "Delete",
|
||||||
|
"more": "More",
|
||||||
"remove": "Remove",
|
"remove": "Remove",
|
||||||
"save": "Save"
|
"save": "Save"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
"add": "Ajouter",
|
"add": "Ajouter",
|
||||||
"cancel": "Annuler",
|
"cancel": "Annuler",
|
||||||
"delete": "Effacer",
|
"delete": "Effacer",
|
||||||
|
"more": null,
|
||||||
"remove": "Supprimer",
|
"remove": "Supprimer",
|
||||||
"save": "Sauvegarder"
|
"save": "Sauvegarder"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
"add": "Lägg till",
|
"add": "Lägg till",
|
||||||
"cancel": "Avbryt",
|
"cancel": "Avbryt",
|
||||||
"delete": "Radera",
|
"delete": "Radera",
|
||||||
|
"more": null,
|
||||||
"remove": "Ta bort",
|
"remove": "Ta bort",
|
||||||
"save": "Spara"
|
"save": "Spara"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -24,6 +24,8 @@ import {
|
|||||||
sidebarPhoto_media_videoMetadata,
|
sidebarPhoto_media_videoMetadata,
|
||||||
} from './__generated__/sidebarPhoto'
|
} from './__generated__/sidebarPhoto'
|
||||||
|
|
||||||
|
import { sidebarDownloadQuery_media_downloads } from './__generated__/sidebarDownloadQuery'
|
||||||
|
|
||||||
const SIDEBAR_MEDIA_QUERY = gql`
|
const SIDEBAR_MEDIA_QUERY = gql`
|
||||||
query sidebarPhoto($id: ID!) {
|
query sidebarPhoto($id: ID!) {
|
||||||
media(id: $id) {
|
media(id: $id) {
|
||||||
@@ -161,13 +163,15 @@ export const MetadataInfo = ({ media }: MediaInfoProps) => {
|
|||||||
x => mediaExif[x] !== null && x != '__typename'
|
x => mediaExif[x] !== null && x != '__typename'
|
||||||
)
|
)
|
||||||
|
|
||||||
const exif = exifKeys.reduce(
|
const exif = exifKeys.reduce((prev, curr) => {
|
||||||
(prev, curr) => ({
|
const value = mediaExif[curr]
|
||||||
|
if (isNil(value)) return prev
|
||||||
|
|
||||||
|
return {
|
||||||
...prev,
|
...prev,
|
||||||
[curr]: mediaExif[curr],
|
[curr]: value,
|
||||||
}),
|
}
|
||||||
{} as { [key: string]: string | number | null }
|
}, {} as { [key: string]: string | number })
|
||||||
)
|
|
||||||
|
|
||||||
if (!isNil(exif.dateShot)) {
|
if (!isNil(exif.dateShot)) {
|
||||||
exif.dateShot = new Date(exif.dateShot).toLocaleString()
|
exif.dateShot = new Date(exif.dateShot).toLocaleString()
|
||||||
@@ -202,7 +206,7 @@ export const MetadataInfo = ({ media }: MediaInfoProps) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
exifItems = exifKeys.map(key => (
|
exifItems = exifKeys.map(key => (
|
||||||
<SidebarItem key={key} name={exifName[key]} value={exif[key]} />
|
<SidebarItem key={key} name={exifName[key]} value={exif[key] as string} />
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -214,13 +218,15 @@ export const MetadataInfo = ({ media }: MediaInfoProps) => {
|
|||||||
|
|
||||||
let metadata = Object.keys(videoMetadata)
|
let metadata = Object.keys(videoMetadata)
|
||||||
.filter(x => !['id', '__typename', 'width', 'height'].includes(x))
|
.filter(x => !['id', '__typename', 'width', 'height'].includes(x))
|
||||||
.reduce(
|
.reduce((prev, curr) => {
|
||||||
(prev, curr) => ({
|
const value = videoMetadata[curr as string]
|
||||||
|
if (isNil(value)) return prev
|
||||||
|
|
||||||
|
return {
|
||||||
...prev,
|
...prev,
|
||||||
[curr]: videoMetadata[curr as string],
|
[curr]: value,
|
||||||
}),
|
}
|
||||||
{} as { [key: string]: string | number | null }
|
}, {} as { [key: string]: string | number })
|
||||||
)
|
|
||||||
|
|
||||||
metadata = {
|
metadata = {
|
||||||
dimensions: `${media.videoMetadata.width}x${media.videoMetadata.height}`,
|
dimensions: `${media.videoMetadata.width}x${media.videoMetadata.height}`,
|
||||||
@@ -228,7 +234,7 @@ export const MetadataInfo = ({ media }: MediaInfoProps) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
videoMetadataItems = Object.keys(metadata).map(key => (
|
videoMetadataItems = Object.keys(metadata).map(key => (
|
||||||
<SidebarItem key={key} name={key} value={metadata[key]} />
|
<SidebarItem key={key} name={key} value={metadata[key] as string} />
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -376,7 +382,7 @@ const SidebarContent = ({ media, hidePreview }: SidebarContentProps) => {
|
|||||||
)}
|
)}
|
||||||
<Name>{media.title}</Name>
|
<Name>{media.title}</Name>
|
||||||
<MetadataInfo media={media} />
|
<MetadataInfo media={media} />
|
||||||
<SidebarDownload photo={media} />
|
<SidebarDownload media={media} />
|
||||||
<SidebarPhotoShare id={media.id} />
|
<SidebarPhotoShare id={media.id} />
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
@@ -403,6 +409,7 @@ export interface MediaSidebarMedia {
|
|||||||
videoMetadata?: sidebarPhoto_media_videoMetadata | null
|
videoMetadata?: sidebarPhoto_media_videoMetadata | null
|
||||||
exif?: sidebarPhoto_media_exif | null
|
exif?: sidebarPhoto_media_exif | null
|
||||||
faces?: sidebarPhoto_media_faces[]
|
faces?: sidebarPhoto_media_faces[]
|
||||||
|
downloads?: sidebarDownloadQuery_media_downloads[]
|
||||||
}
|
}
|
||||||
|
|
||||||
type MediaSidebarType = {
|
type MediaSidebarType = {
|
||||||
|
|||||||
@@ -211,7 +211,7 @@ const ShareItemMoreDropdown = ({
|
|||||||
// onClose={() => setDropdownOpen(false)}
|
// onClose={() => setDropdownOpen(false)}
|
||||||
// open={dropdownOpen}
|
// open={dropdownOpen}
|
||||||
button
|
button
|
||||||
text="More"
|
text={t('general.action.more', 'More')}
|
||||||
closeOnChange={false}
|
closeOnChange={false}
|
||||||
closeOnBlur={false}
|
closeOnBlur={false}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -6,6 +6,13 @@ import { MessageState } from '../messages/Messages'
|
|||||||
import { useLazyQuery, gql } from '@apollo/client'
|
import { useLazyQuery, gql } from '@apollo/client'
|
||||||
import { authToken } from '../../helpers/authentication'
|
import { authToken } from '../../helpers/authentication'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import { TranslationFn } from '../../localization'
|
||||||
|
import { MediaSidebarMedia } from './MediaSidebar'
|
||||||
|
import {
|
||||||
|
sidebarDownloadQuery,
|
||||||
|
sidebarDownloadQueryVariables,
|
||||||
|
sidebarDownloadQuery_media_downloads,
|
||||||
|
} from './__generated__/sidebarDownloadQuery'
|
||||||
|
|
||||||
export const SIDEBAR_DOWNLOAD_QUERY = gql`
|
export const SIDEBAR_DOWNLOAD_QUERY = gql`
|
||||||
query sidebarDownloadQuery($mediaId: ID!) {
|
query sidebarDownloadQuery($mediaId: ID!) {
|
||||||
@@ -24,10 +31,12 @@ export const SIDEBAR_DOWNLOAD_QUERY = gql`
|
|||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
const formatBytes = t => bytes => {
|
const formatBytes = (t: TranslationFn) => (bytes: number) => {
|
||||||
if (bytes == 0) return '0 Byte'
|
if (bytes == 0)
|
||||||
const i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024)))
|
return t('sidebar.download.filesize.byte', '{{count}} Byte', { count: 0 })
|
||||||
const count = Math.round(bytes / Math.pow(1024, i), 2)
|
|
||||||
|
const i = Math.floor(Math.log(bytes) / Math.log(1024))
|
||||||
|
const count = Math.round(bytes / Math.pow(1024, i))
|
||||||
|
|
||||||
switch (i) {
|
switch (i) {
|
||||||
case 0:
|
case 0:
|
||||||
@@ -46,7 +55,7 @@ const formatBytes = t => bytes => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const downloadMedia = t => async url => {
|
const downloadMedia = (t: TranslationFn) => async (url: string) => {
|
||||||
const imgUrl = new URL(url, location.origin)
|
const imgUrl = new URL(url, location.origin)
|
||||||
|
|
||||||
if (authToken() == null) {
|
if (authToken() == null) {
|
||||||
@@ -68,15 +77,32 @@ const downloadMedia = t => async url => {
|
|||||||
blob = await response.blob()
|
blob = await response.blob()
|
||||||
}
|
}
|
||||||
|
|
||||||
const filename = url.match(/[^/]*$/)[0]
|
if (blob == null) {
|
||||||
|
console.log('Blob is null canceling')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const filenameMatch = url.match(/[^/]*$/)
|
||||||
|
|
||||||
|
if (filenameMatch == null) {
|
||||||
|
console.error('Could not extract filename', url)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const filename = filenameMatch[0]
|
||||||
downloadBlob(blob, filename)
|
downloadBlob(blob, filename)
|
||||||
}
|
}
|
||||||
|
|
||||||
const downloadMediaShowProgress = t => async response => {
|
const downloadMediaShowProgress = (t: TranslationFn) => async (
|
||||||
|
response: Response
|
||||||
|
) => {
|
||||||
const totalBytes = Number(response.headers.get('content-length'))
|
const totalBytes = Number(response.headers.get('content-length'))
|
||||||
const reader = response.body.getReader()
|
const reader = response.body?.getReader()
|
||||||
let data = new Uint8Array(totalBytes)
|
const data = new Uint8Array(totalBytes)
|
||||||
|
|
||||||
|
if (reader == null) {
|
||||||
|
throw new Error('Download reader is null')
|
||||||
|
}
|
||||||
|
|
||||||
let canceled = false
|
let canceled = false
|
||||||
const onDismiss = () => {
|
const onDismiss = () => {
|
||||||
@@ -84,9 +110,9 @@ const downloadMediaShowProgress = t => async response => {
|
|||||||
reader.cancel('Download canceled by user')
|
reader.cancel('Download canceled by user')
|
||||||
}
|
}
|
||||||
|
|
||||||
const notifKey = Math.random().toString(26)
|
const notifyKey = Math.random().toString(26)
|
||||||
MessageState.add({
|
MessageState.add({
|
||||||
key: notifKey,
|
key: notifyKey,
|
||||||
type: 'progress',
|
type: 'progress',
|
||||||
onDismiss,
|
onDismiss,
|
||||||
props: {
|
props: {
|
||||||
@@ -108,7 +134,7 @@ const downloadMediaShowProgress = t => async response => {
|
|||||||
receivedBytes += result.value ? result.value.length : 0
|
receivedBytes += result.value ? result.value.length : 0
|
||||||
|
|
||||||
MessageState.add({
|
MessageState.add({
|
||||||
key: notifKey,
|
key: notifyKey,
|
||||||
type: 'progress',
|
type: 'progress',
|
||||||
onDismiss,
|
onDismiss,
|
||||||
props: {
|
props: {
|
||||||
@@ -126,7 +152,7 @@ const downloadMediaShowProgress = t => async response => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
MessageState.add({
|
MessageState.add({
|
||||||
key: notifKey,
|
key: notifyKey,
|
||||||
type: 'progress',
|
type: 'progress',
|
||||||
props: {
|
props: {
|
||||||
header: 'Downloading photo completed',
|
header: 'Downloading photo completed',
|
||||||
@@ -137,20 +163,20 @@ const downloadMediaShowProgress = t => async response => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
MessageState.removeKey(notifKey)
|
MessageState.removeKey(notifyKey)
|
||||||
}, 2000)
|
}, 2000)
|
||||||
|
|
||||||
const content = new Blob([data.buffer], {
|
const content = new Blob([data.buffer], {
|
||||||
type: response.headers.get('content-type'),
|
type: response.headers.get('content-type') || undefined,
|
||||||
})
|
})
|
||||||
|
|
||||||
return content
|
return content
|
||||||
}
|
}
|
||||||
|
|
||||||
const downloadBlob = async (blob, filename) => {
|
const downloadBlob = async (blob: Blob, filename: string) => {
|
||||||
let objectUrl = window.URL.createObjectURL(blob)
|
const objectUrl = window.URL.createObjectURL(blob)
|
||||||
|
|
||||||
let anchor = document.createElement('a')
|
const anchor = document.createElement('a')
|
||||||
document.body.appendChild(anchor)
|
document.body.appendChild(anchor)
|
||||||
|
|
||||||
anchor.href = objectUrl
|
anchor.href = objectUrl
|
||||||
@@ -166,36 +192,43 @@ const DownloadTableRow = styled(Table.Row)`
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
`
|
`
|
||||||
|
|
||||||
const SidebarDownload = ({ photo }) => {
|
type SidebarDownladProps = {
|
||||||
|
media: MediaSidebarMedia
|
||||||
|
}
|
||||||
|
|
||||||
|
const SidebarDownload = ({ media }: SidebarDownladProps) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
if (!photo || !photo.id) return null
|
if (!media || !media.id) return null
|
||||||
|
|
||||||
const [
|
const [loadPhotoDownloads, { called, loading, data }] = useLazyQuery<
|
||||||
loadPhotoDownloads,
|
sidebarDownloadQuery,
|
||||||
{ called, loading, data },
|
sidebarDownloadQueryVariables
|
||||||
] = useLazyQuery(SIDEBAR_DOWNLOAD_QUERY, { variables: { mediaId: photo.id } })
|
>(SIDEBAR_DOWNLOAD_QUERY, { variables: { mediaId: media.id } })
|
||||||
|
|
||||||
let downloads = []
|
let downloads: sidebarDownloadQuery_media_downloads[] = []
|
||||||
|
|
||||||
if (called) {
|
if (called) {
|
||||||
if (!loading) {
|
if (!loading) {
|
||||||
downloads = data && data.media.downloads
|
downloads = (data && data.media.downloads) || []
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (!photo.downloads) {
|
if (!media.downloads) {
|
||||||
loadPhotoDownloads()
|
loadPhotoDownloads()
|
||||||
} else {
|
} else {
|
||||||
downloads = photo.downloads
|
downloads = media.downloads
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const extractExtension = url => {
|
const extractExtension = (url: string) => {
|
||||||
return url.split(/[#?]/)[0].split('.').pop().trim().toLowerCase()
|
const urlMatch = url.split(/[#?]/)
|
||||||
|
if (urlMatch == null) return
|
||||||
|
|
||||||
|
return urlMatch[0].split('.').pop()?.trim().toLowerCase()
|
||||||
}
|
}
|
||||||
|
|
||||||
const download = downloadMedia(t)
|
const download = downloadMedia(t)
|
||||||
const bytes = formatBytes(t)
|
const bytes = formatBytes(t)
|
||||||
let downloadRows = downloads.map(x => (
|
const downloadRows = downloads.map(x => (
|
||||||
<DownloadTableRow
|
<DownloadTableRow
|
||||||
key={x.mediaUrl.url}
|
key={x.mediaUrl.url}
|
||||||
onClick={() => download(x.mediaUrl.url)}
|
onClick={() => download(x.mediaUrl.url)}
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import PropTypes from 'prop-types'
|
|
||||||
import styled from 'styled-components'
|
import styled from 'styled-components'
|
||||||
|
|
||||||
const ItemName = styled.div`
|
const ItemName = styled.div`
|
||||||
@@ -17,16 +16,16 @@ const ItemValue = styled.div`
|
|||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
`
|
`
|
||||||
|
|
||||||
const SidebarItem = ({ name, value }) => (
|
type SidebarItemProps = {
|
||||||
|
name: string
|
||||||
|
value: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const SidebarItem = ({ name, value }: SidebarItemProps) => (
|
||||||
<div>
|
<div>
|
||||||
<ItemName>{name}</ItemName>
|
<ItemName>{name}</ItemName>
|
||||||
<ItemValue>{value}</ItemValue>
|
<ItemValue>{value}</ItemValue>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|
||||||
SidebarItem.propTypes = {
|
|
||||||
name: PropTypes.string.isRequired,
|
|
||||||
value: PropTypes.any.isRequired,
|
|
||||||
}
|
|
||||||
|
|
||||||
export default SidebarItem
|
export default SidebarItem
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
/* Visit https://aka.ms/tsconfig.json to read more about this file */
|
/* Visit https://aka.ms/tsconfig.json to read more about this file */
|
||||||
|
|
||||||
/* Basic Options */
|
/* Basic Options */
|
||||||
"incremental": true /* Enable incremental compilation */,
|
// "incremental": true /* Enable incremental compilation */,
|
||||||
"target": "es5" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */,
|
"target": "es5" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */,
|
||||||
"module": "commonjs" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */,
|
"module": "commonjs" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */,
|
||||||
"lib": [
|
"lib": [
|
||||||
|
|||||||
Reference in New Issue
Block a user