diff --git a/ui/extractedTranslations/da/translation.json b/ui/extractedTranslations/da/translation.json
index bfa96825..1f46bd61 100644
--- a/ui/extractedTranslations/da/translation.json
+++ b/ui/extractedTranslations/da/translation.json
@@ -17,6 +17,7 @@
"add": "Tilføj",
"cancel": "Annuller",
"delete": "Slet",
+ "more": "Mere",
"remove": "Fjern",
"save": "Gem"
},
@@ -76,7 +77,7 @@
"title": "Billeder"
},
"places_page": {
- "title": null
+ "title": "Kort"
},
"routes": {
"page_not_found": "Side ikke fundet"
@@ -169,7 +170,7 @@
},
"sidebar": {
"album": {
- "title": "Album indstillinger"
+ "title": "Indstillinger for album"
},
"download": {
"filesize": {
@@ -223,7 +224,7 @@
"flash": "Blitz",
"focal_length": "Brændvidde",
"iso": "ISO",
- "lens": "Lense",
+ "lens": "Objektiv",
"maker": "Mærke"
}
}
diff --git a/ui/extractedTranslations/en/translation.json b/ui/extractedTranslations/en/translation.json
index 820513e0..418843d2 100644
--- a/ui/extractedTranslations/en/translation.json
+++ b/ui/extractedTranslations/en/translation.json
@@ -17,6 +17,7 @@
"add": "Add",
"cancel": "Cancel",
"delete": "Delete",
+ "more": "More",
"remove": "Remove",
"save": "Save"
},
diff --git a/ui/extractedTranslations/fr/translation.json b/ui/extractedTranslations/fr/translation.json
index 5a533d20..94d9d225 100644
--- a/ui/extractedTranslations/fr/translation.json
+++ b/ui/extractedTranslations/fr/translation.json
@@ -17,6 +17,7 @@
"add": "Ajouter",
"cancel": "Annuler",
"delete": "Effacer",
+ "more": null,
"remove": "Supprimer",
"save": "Sauvegarder"
},
diff --git a/ui/extractedTranslations/sv/translation.json b/ui/extractedTranslations/sv/translation.json
index 72be0a1c..dd8bbee5 100644
--- a/ui/extractedTranslations/sv/translation.json
+++ b/ui/extractedTranslations/sv/translation.json
@@ -17,6 +17,7 @@
"add": "Lägg till",
"cancel": "Avbryt",
"delete": "Radera",
+ "more": null,
"remove": "Ta bort",
"save": "Spara"
},
diff --git a/ui/src/components/sidebar/MediaSidebar.tsx b/ui/src/components/sidebar/MediaSidebar.tsx
index 80b28e6d..dedb8f85 100644
--- a/ui/src/components/sidebar/MediaSidebar.tsx
+++ b/ui/src/components/sidebar/MediaSidebar.tsx
@@ -24,6 +24,8 @@ import {
sidebarPhoto_media_videoMetadata,
} from './__generated__/sidebarPhoto'
+import { sidebarDownloadQuery_media_downloads } from './__generated__/sidebarDownloadQuery'
+
const SIDEBAR_MEDIA_QUERY = gql`
query sidebarPhoto($id: ID!) {
media(id: $id) {
@@ -161,13 +163,15 @@ export const MetadataInfo = ({ media }: MediaInfoProps) => {
x => mediaExif[x] !== null && x != '__typename'
)
- const exif = exifKeys.reduce(
- (prev, curr) => ({
+ const exif = exifKeys.reduce((prev, curr) => {
+ const value = mediaExif[curr]
+ if (isNil(value)) return prev
+
+ return {
...prev,
- [curr]: mediaExif[curr],
- }),
- {} as { [key: string]: string | number | null }
- )
+ [curr]: value,
+ }
+ }, {} as { [key: string]: string | number })
if (!isNil(exif.dateShot)) {
exif.dateShot = new Date(exif.dateShot).toLocaleString()
@@ -202,7 +206,7 @@ export const MetadataInfo = ({ media }: MediaInfoProps) => {
}
exifItems = exifKeys.map(key => (
-
+
))
}
@@ -214,13 +218,15 @@ export const MetadataInfo = ({ media }: MediaInfoProps) => {
let metadata = Object.keys(videoMetadata)
.filter(x => !['id', '__typename', 'width', 'height'].includes(x))
- .reduce(
- (prev, curr) => ({
+ .reduce((prev, curr) => {
+ const value = videoMetadata[curr as string]
+ if (isNil(value)) return prev
+
+ return {
...prev,
- [curr]: videoMetadata[curr as string],
- }),
- {} as { [key: string]: string | number | null }
- )
+ [curr]: value,
+ }
+ }, {} as { [key: string]: string | number })
metadata = {
dimensions: `${media.videoMetadata.width}x${media.videoMetadata.height}`,
@@ -228,7 +234,7 @@ export const MetadataInfo = ({ media }: MediaInfoProps) => {
}
videoMetadataItems = Object.keys(metadata).map(key => (
-
+
))
}
@@ -376,7 +382,7 @@ const SidebarContent = ({ media, hidePreview }: SidebarContentProps) => {
)}
{media.title}
-
+
)
@@ -403,6 +409,7 @@ export interface MediaSidebarMedia {
videoMetadata?: sidebarPhoto_media_videoMetadata | null
exif?: sidebarPhoto_media_exif | null
faces?: sidebarPhoto_media_faces[]
+ downloads?: sidebarDownloadQuery_media_downloads[]
}
type MediaSidebarType = {
diff --git a/ui/src/components/sidebar/Sharing.tsx b/ui/src/components/sidebar/Sharing.tsx
index 6c7a272e..457bbeb4 100644
--- a/ui/src/components/sidebar/Sharing.tsx
+++ b/ui/src/components/sidebar/Sharing.tsx
@@ -211,7 +211,7 @@ const ShareItemMoreDropdown = ({
// onClose={() => setDropdownOpen(false)}
// open={dropdownOpen}
button
- text="More"
+ text={t('general.action.more', 'More')}
closeOnChange={false}
closeOnBlur={false}
>
diff --git a/ui/src/components/sidebar/SidebarDownload.js b/ui/src/components/sidebar/SidebarDownload.tsx
similarity index 68%
rename from ui/src/components/sidebar/SidebarDownload.js
rename to ui/src/components/sidebar/SidebarDownload.tsx
index 9e6c5384..e50f7887 100644
--- a/ui/src/components/sidebar/SidebarDownload.js
+++ b/ui/src/components/sidebar/SidebarDownload.tsx
@@ -6,6 +6,13 @@ import { MessageState } from '../messages/Messages'
import { useLazyQuery, gql } from '@apollo/client'
import { authToken } from '../../helpers/authentication'
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`
query sidebarDownloadQuery($mediaId: ID!) {
@@ -24,10 +31,12 @@ export const SIDEBAR_DOWNLOAD_QUERY = gql`
}
`
-const formatBytes = t => bytes => {
- if (bytes == 0) return '0 Byte'
- const i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024)))
- const count = Math.round(bytes / Math.pow(1024, i), 2)
+const formatBytes = (t: TranslationFn) => (bytes: number) => {
+ if (bytes == 0)
+ return t('sidebar.download.filesize.byte', '{{count}} Byte', { count: 0 })
+
+ const i = Math.floor(Math.log(bytes) / Math.log(1024))
+ const count = Math.round(bytes / Math.pow(1024, i))
switch (i) {
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)
if (authToken() == null) {
@@ -68,15 +77,32 @@ const downloadMedia = t => async url => {
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)
}
-const downloadMediaShowProgress = t => async response => {
+const downloadMediaShowProgress = (t: TranslationFn) => async (
+ response: Response
+) => {
const totalBytes = Number(response.headers.get('content-length'))
- const reader = response.body.getReader()
- let data = new Uint8Array(totalBytes)
+ const reader = response.body?.getReader()
+ const data = new Uint8Array(totalBytes)
+
+ if (reader == null) {
+ throw new Error('Download reader is null')
+ }
let canceled = false
const onDismiss = () => {
@@ -84,9 +110,9 @@ const downloadMediaShowProgress = t => async response => {
reader.cancel('Download canceled by user')
}
- const notifKey = Math.random().toString(26)
+ const notifyKey = Math.random().toString(26)
MessageState.add({
- key: notifKey,
+ key: notifyKey,
type: 'progress',
onDismiss,
props: {
@@ -108,7 +134,7 @@ const downloadMediaShowProgress = t => async response => {
receivedBytes += result.value ? result.value.length : 0
MessageState.add({
- key: notifKey,
+ key: notifyKey,
type: 'progress',
onDismiss,
props: {
@@ -126,7 +152,7 @@ const downloadMediaShowProgress = t => async response => {
}
MessageState.add({
- key: notifKey,
+ key: notifyKey,
type: 'progress',
props: {
header: 'Downloading photo completed',
@@ -137,20 +163,20 @@ const downloadMediaShowProgress = t => async response => {
})
setTimeout(() => {
- MessageState.removeKey(notifKey)
+ MessageState.removeKey(notifyKey)
}, 2000)
const content = new Blob([data.buffer], {
- type: response.headers.get('content-type'),
+ type: response.headers.get('content-type') || undefined,
})
return content
}
-const downloadBlob = async (blob, filename) => {
- let objectUrl = window.URL.createObjectURL(blob)
+const downloadBlob = async (blob: Blob, filename: string) => {
+ const objectUrl = window.URL.createObjectURL(blob)
- let anchor = document.createElement('a')
+ const anchor = document.createElement('a')
document.body.appendChild(anchor)
anchor.href = objectUrl
@@ -166,36 +192,43 @@ const DownloadTableRow = styled(Table.Row)`
cursor: pointer;
`
-const SidebarDownload = ({ photo }) => {
+type SidebarDownladProps = {
+ media: MediaSidebarMedia
+}
+
+const SidebarDownload = ({ media }: SidebarDownladProps) => {
const { t } = useTranslation()
- if (!photo || !photo.id) return null
+ if (!media || !media.id) return null
- const [
- loadPhotoDownloads,
- { called, loading, data },
- ] = useLazyQuery(SIDEBAR_DOWNLOAD_QUERY, { variables: { mediaId: photo.id } })
+ const [loadPhotoDownloads, { called, loading, data }] = useLazyQuery<
+ sidebarDownloadQuery,
+ sidebarDownloadQueryVariables
+ >(SIDEBAR_DOWNLOAD_QUERY, { variables: { mediaId: media.id } })
- let downloads = []
+ let downloads: sidebarDownloadQuery_media_downloads[] = []
if (called) {
if (!loading) {
- downloads = data && data.media.downloads
+ downloads = (data && data.media.downloads) || []
}
} else {
- if (!photo.downloads) {
+ if (!media.downloads) {
loadPhotoDownloads()
} else {
- downloads = photo.downloads
+ downloads = media.downloads
}
}
- const extractExtension = url => {
- return url.split(/[#?]/)[0].split('.').pop().trim().toLowerCase()
+ const extractExtension = (url: string) => {
+ const urlMatch = url.split(/[#?]/)
+ if (urlMatch == null) return
+
+ return urlMatch[0].split('.').pop()?.trim().toLowerCase()
}
const download = downloadMedia(t)
const bytes = formatBytes(t)
- let downloadRows = downloads.map(x => (
+ const downloadRows = downloads.map(x => (
download(x.mediaUrl.url)}
diff --git a/ui/src/components/sidebar/SidebarItem.js b/ui/src/components/sidebar/SidebarItem.tsx
similarity index 71%
rename from ui/src/components/sidebar/SidebarItem.js
rename to ui/src/components/sidebar/SidebarItem.tsx
index 4d5f4d08..1608f733 100644
--- a/ui/src/components/sidebar/SidebarItem.js
+++ b/ui/src/components/sidebar/SidebarItem.tsx
@@ -1,5 +1,4 @@
import React from 'react'
-import PropTypes from 'prop-types'
import styled from 'styled-components'
const ItemName = styled.div`
@@ -17,16 +16,16 @@ const ItemValue = styled.div`
font-size: 1rem;
`
-const SidebarItem = ({ name, value }) => (
+type SidebarItemProps = {
+ name: string
+ value: string
+}
+
+const SidebarItem = ({ name, value }: SidebarItemProps) => (
{name}
{value}
)
-SidebarItem.propTypes = {
- name: PropTypes.string.isRequired,
- value: PropTypes.any.isRequired,
-}
-
export default SidebarItem
diff --git a/ui/tsconfig.json b/ui/tsconfig.json
index 07e7d9c0..49652a22 100644
--- a/ui/tsconfig.json
+++ b/ui/tsconfig.json
@@ -3,7 +3,7 @@
/* Visit https://aka.ms/tsconfig.json to read more about this file */
/* 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'. */,
"module": "commonjs" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */,
"lib": [