Add more translations
This commit is contained in:
parent
12df24f366
commit
e530ce5555
|
@ -1,4 +1,14 @@
|
|||
{
|
||||
"album_filter": {
|
||||
"only_favorites": "Vis kun favoritter",
|
||||
"sort_by": "Sorter efter",
|
||||
"sorting_options": {
|
||||
"date_imported": "Dato for importering",
|
||||
"date_shot": "Dato",
|
||||
"title": "Titel",
|
||||
"type": "Type"
|
||||
}
|
||||
},
|
||||
"albums_page": {
|
||||
"title": "Album"
|
||||
},
|
||||
|
@ -6,8 +16,24 @@
|
|||
"action": {
|
||||
"add": "Tilføj",
|
||||
"cancel": "Annuller",
|
||||
"remove": "Slet",
|
||||
"delete": "Slet",
|
||||
"remove": "Fjern",
|
||||
"save": "Gem"
|
||||
},
|
||||
"loading": {
|
||||
"default": "Loader...",
|
||||
"shares": null
|
||||
}
|
||||
},
|
||||
"header": {
|
||||
"search": {
|
||||
"loading": "Loader resultater...",
|
||||
"no_results": "Fandt ingen resultater",
|
||||
"placeholder": "Søg",
|
||||
"result_type": {
|
||||
"albums": "Albums",
|
||||
"photos": "Billeder"
|
||||
}
|
||||
}
|
||||
},
|
||||
"loading": {
|
||||
|
@ -96,6 +122,76 @@
|
|||
"title": "Brugere"
|
||||
}
|
||||
},
|
||||
"sidebar": {
|
||||
"album": {
|
||||
"title": "Album indstillinger"
|
||||
},
|
||||
"download": {
|
||||
"filesize": {
|
||||
"byte": "{{count}} Byte",
|
||||
"byte_plural": "{{count}} Bytes",
|
||||
"giga_byte": "{{count}} GB",
|
||||
"kilo_byte": "{{count}} KB",
|
||||
"mega_byte": "{{count}} MB",
|
||||
"tera_byte": "{{count}} TB"
|
||||
},
|
||||
"table_columns": {
|
||||
"dimensions": "Dimension",
|
||||
"file_size": "Størrelse",
|
||||
"file_type": "Type",
|
||||
"name": "Navn"
|
||||
},
|
||||
"title": "Download"
|
||||
},
|
||||
"media": {
|
||||
"exif": {
|
||||
"exposure_program": {
|
||||
"action_program": "Actionprogram",
|
||||
"aperture_priority": "Blændeprioritet",
|
||||
"bulb": "Bulb",
|
||||
"creative_program": "Kreativ program",
|
||||
"landscape_mode": "Landskabsmode",
|
||||
"manual": "Manuel",
|
||||
"normal_program": "Normal program",
|
||||
"not_defined": "Ikke defineret",
|
||||
"portrait_mode": "Portræt mode",
|
||||
"shutter_priority": "Lukkerprioritet"
|
||||
},
|
||||
"flash": {
|
||||
"auto": "Auto",
|
||||
"did_not_fire": "Blitz affyrede ikke",
|
||||
"fired": "Affyrede",
|
||||
"no_flash": "Ingen blitz",
|
||||
"no_flash_function": "Ingen blitz-funktion",
|
||||
"off": "Slukket",
|
||||
"on": "Tændt",
|
||||
"red_eye_reduction": "Røde øjne reduktion",
|
||||
"return_detected": "Retur registreret",
|
||||
"return_not_detected": "Retur ikke registreret"
|
||||
},
|
||||
"name": {
|
||||
"aperture": "Blænde",
|
||||
"camera": "Kamera",
|
||||
"date_shot": "Dato",
|
||||
"exposure": "Lukketid",
|
||||
"exposure_program": "Lukketid program",
|
||||
"flash": "Blitz",
|
||||
"focal_length": "Fokallængde",
|
||||
"iso": "ISO",
|
||||
"lens": "Lense",
|
||||
"maker": "Mærke"
|
||||
}
|
||||
}
|
||||
},
|
||||
"sharing": {
|
||||
"add_share": "Tilføj deling",
|
||||
"copy_link": "Kopier link",
|
||||
"no_shares_found": "Ingen delinger fundet",
|
||||
"public_link": "Offentligt link",
|
||||
"table_header": "Offentlige delinger",
|
||||
"title": "Indstillinger for deling"
|
||||
}
|
||||
},
|
||||
"sidemenu": {
|
||||
"albums": "Albums",
|
||||
"logout": "Log af",
|
||||
|
|
|
@ -1,4 +1,14 @@
|
|||
{
|
||||
"album_filter": {
|
||||
"only_favorites": "Show only favorites",
|
||||
"sort_by": "Sort by",
|
||||
"sorting_options": {
|
||||
"date_imported": "Date imported",
|
||||
"date_shot": "Date shot",
|
||||
"title": "Title",
|
||||
"type": "Kind"
|
||||
}
|
||||
},
|
||||
"albums_page": {
|
||||
"title": "Albums"
|
||||
},
|
||||
|
@ -6,8 +16,24 @@
|
|||
"action": {
|
||||
"add": "Add",
|
||||
"cancel": "Cancel",
|
||||
"delete": "Delete",
|
||||
"remove": "Remove",
|
||||
"save": "Save"
|
||||
},
|
||||
"loading": {
|
||||
"default": "Loading...",
|
||||
"shares": "Loading shares..."
|
||||
}
|
||||
},
|
||||
"header": {
|
||||
"search": {
|
||||
"loading": "Loading results...",
|
||||
"no_results": "No results found",
|
||||
"placeholder": "Search",
|
||||
"result_type": {
|
||||
"albums": "Albums",
|
||||
"photos": "Photos"
|
||||
}
|
||||
}
|
||||
},
|
||||
"loading": {
|
||||
|
@ -96,6 +122,76 @@
|
|||
"title": "Users"
|
||||
}
|
||||
},
|
||||
"sidebar": {
|
||||
"album": {
|
||||
"title": "Album options"
|
||||
},
|
||||
"download": {
|
||||
"filesize": {
|
||||
"byte": "{{count}} Byte",
|
||||
"byte_plural": "{{count}} Bytes",
|
||||
"giga_byte": "{{count}} GB",
|
||||
"kilo_byte": "{{count}} KB",
|
||||
"mega_byte": "{{count}} MB",
|
||||
"tera_byte": "{{count}} TB"
|
||||
},
|
||||
"table_columns": {
|
||||
"dimensions": "Dimensions",
|
||||
"file_size": "Size",
|
||||
"file_type": "Type",
|
||||
"name": "Name"
|
||||
},
|
||||
"title": "Download"
|
||||
},
|
||||
"media": {
|
||||
"exif": {
|
||||
"exposure_program": {
|
||||
"action_program": "Action program",
|
||||
"aperture_priority": "Aperture priority",
|
||||
"bulb": "Bulb",
|
||||
"creative_program": "Creative program",
|
||||
"landscape_mode": "Landscape mode",
|
||||
"manual": "Manual",
|
||||
"normal_program": "Normal program",
|
||||
"not_defined": "Not defined",
|
||||
"portrait_mode": "Portrait mode",
|
||||
"shutter_priority": "Shutter priority"
|
||||
},
|
||||
"flash": {
|
||||
"auto": "Auto",
|
||||
"did_not_fire": "Did not fire",
|
||||
"fired": "Fired",
|
||||
"no_flash": "No Flash",
|
||||
"no_flash_function": "No flash function",
|
||||
"off": "Off",
|
||||
"on": "On",
|
||||
"red_eye_reduction": "Red-eye reduction",
|
||||
"return_detected": "Return detected",
|
||||
"return_not_detected": "Return not detected"
|
||||
},
|
||||
"name": {
|
||||
"aperture": "Aperture",
|
||||
"camera": "Camera",
|
||||
"date_shot": "Date shot",
|
||||
"exposure": "Exposure",
|
||||
"exposure_program": "Program",
|
||||
"flash": "Flash",
|
||||
"focal_length": "Focal length",
|
||||
"iso": "ISO",
|
||||
"lens": "Lens",
|
||||
"maker": "Maker"
|
||||
}
|
||||
}
|
||||
},
|
||||
"sharing": {
|
||||
"add_share": "Add shares",
|
||||
"copy_link": "Copy Link",
|
||||
"no_shares_found": "No shares found",
|
||||
"public_link": "Public Link",
|
||||
"table_header": "Public shares",
|
||||
"title": "Sharing options"
|
||||
}
|
||||
},
|
||||
"sidemenu": {
|
||||
"albums": "Albums",
|
||||
"logout": "Log out",
|
||||
|
|
|
@ -3,29 +3,7 @@ import { authToken } from '../helpers/authentication'
|
|||
import { Checkbox, Dropdown, Button, Icon } from 'semantic-ui-react'
|
||||
import styled from 'styled-components'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
const sortingOptions = [
|
||||
{
|
||||
key: 'date_shot',
|
||||
value: 'date_shot',
|
||||
text: 'Date shot',
|
||||
},
|
||||
{
|
||||
key: 'updated_at',
|
||||
value: 'updated_at',
|
||||
text: 'Date imported',
|
||||
},
|
||||
{
|
||||
key: 'title',
|
||||
value: 'title',
|
||||
text: 'Title',
|
||||
},
|
||||
{
|
||||
key: 'type',
|
||||
value: 'type',
|
||||
text: 'Kind',
|
||||
},
|
||||
]
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
const FavoritesCheckboxStyle = styled(Checkbox)`
|
||||
margin-bottom: 16px;
|
||||
|
@ -56,14 +34,18 @@ const FavoritesCheckboxStyle = styled(Checkbox)`
|
|||
}
|
||||
`
|
||||
|
||||
export const FavoritesCheckbox = ({ onlyFavorites, setOnlyFavorites }) => (
|
||||
<FavoritesCheckboxStyle
|
||||
toggle
|
||||
label="Show only favorites"
|
||||
checked={onlyFavorites}
|
||||
onChange={(e, result) => setOnlyFavorites(result.checked)}
|
||||
/>
|
||||
)
|
||||
export const FavoritesCheckbox = ({ onlyFavorites, setOnlyFavorites }) => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
return (
|
||||
<FavoritesCheckboxStyle
|
||||
toggle
|
||||
label={t('album_filter.only_favorites', 'Show only favorites')}
|
||||
checked={onlyFavorites}
|
||||
onChange={(e, result) => setOnlyFavorites(result.checked)}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
FavoritesCheckbox.propTypes = {
|
||||
onlyFavorites: PropTypes.bool.isRequired,
|
||||
|
@ -75,17 +57,46 @@ const OrderDirectionButton = styled(Button)`
|
|||
margin-left: 10px !important;
|
||||
`
|
||||
|
||||
const SortByLabel = styled.strong`
|
||||
margin-left: 4px;
|
||||
margin-right: 6px;
|
||||
`
|
||||
|
||||
const AlbumFilter = ({
|
||||
onlyFavorites,
|
||||
setOnlyFavorites,
|
||||
setOrdering,
|
||||
ordering,
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
const onChangeOrderDirection = (e, data) => {
|
||||
const direction = data.children.props.name === 'arrow up' ? 'DESC' : 'ASC'
|
||||
setOrdering({ orderDirection: direction })
|
||||
}
|
||||
|
||||
const sortingOptions = [
|
||||
{
|
||||
key: 'date_shot',
|
||||
value: 'date_shot',
|
||||
text: t('album_filter.sorting_options.date_shot', 'Date shot'),
|
||||
},
|
||||
{
|
||||
key: 'updated_at',
|
||||
value: 'updated_at',
|
||||
text: t('album_filter.sorting_options.date_imported', 'Date imported'),
|
||||
},
|
||||
{
|
||||
key: 'title',
|
||||
value: 'title',
|
||||
text: t('album_filter.sorting_options.title', 'Title'),
|
||||
},
|
||||
{
|
||||
key: 'type',
|
||||
value: 'type',
|
||||
text: t('album_filter.sorting_options.type', 'Kind'),
|
||||
},
|
||||
]
|
||||
|
||||
return (
|
||||
<>
|
||||
{authToken() && (
|
||||
|
@ -94,7 +105,7 @@ const AlbumFilter = ({
|
|||
setOnlyFavorites={setOnlyFavorites}
|
||||
/>
|
||||
)}
|
||||
<strong> Sort by </strong>
|
||||
<SortByLabel>{t('album_filter.sort_by', 'Sort by')}</SortByLabel>
|
||||
<Dropdown
|
||||
selection
|
||||
options={sortingOptions}
|
||||
|
|
|
@ -5,6 +5,7 @@ import { useLazyQuery, gql } from '@apollo/client'
|
|||
import { debounce } from '../../helpers/utils'
|
||||
import { ProtectedImage } from '../photoGallery/ProtectedMedia'
|
||||
import { NavLink } from 'react-router-dom'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
const Container = styled.div`
|
||||
height: 60px;
|
||||
|
@ -77,6 +78,7 @@ const SEARCH_QUERY = gql`
|
|||
`
|
||||
|
||||
const SearchBar = () => {
|
||||
const { t } = useTranslation()
|
||||
const [fetchSearches, fetchResult] = useLazyQuery(SEARCH_QUERY)
|
||||
const [query, setQuery] = useState('')
|
||||
const [fetched, setFetched] = useState(false)
|
||||
|
@ -111,7 +113,11 @@ const SearchBar = () => {
|
|||
|
||||
return (
|
||||
<Container>
|
||||
<SearchField type="search" placeholder="Search" onChange={fetchEvent} />
|
||||
<SearchField
|
||||
type="search"
|
||||
placeholder={t('header.search.placeholder', 'Search')}
|
||||
onChange={fetchEvent}
|
||||
/>
|
||||
{results}
|
||||
</Container>
|
||||
)
|
||||
|
@ -123,6 +129,7 @@ const ResultTitle = styled.h1`
|
|||
`
|
||||
|
||||
const SearchResults = ({ result }) => {
|
||||
const { t } = useTranslation()
|
||||
const { data, loading } = result
|
||||
const query = data && data.search.query
|
||||
|
||||
|
@ -130,9 +137,9 @@ const SearchResults = ({ result }) => {
|
|||
const albums = (data && data.search.albums) || []
|
||||
|
||||
let message = null
|
||||
if (loading) message = 'Loading results...'
|
||||
if (loading) message = t('header.search.loading', 'Loading results...')
|
||||
else if (data && media.length == 0 && albums.length == 0)
|
||||
message = 'No results found'
|
||||
message = t('header.search.no_results', 'No results found')
|
||||
|
||||
const albumElements = albums.map(album => (
|
||||
<AlbumRow key={album.id} query={query} album={album} />
|
||||
|
@ -151,9 +158,17 @@ const SearchResults = ({ result }) => {
|
|||
show={data}
|
||||
>
|
||||
{message}
|
||||
{albumElements.length > 0 && <ResultTitle>Albums</ResultTitle>}
|
||||
{albumElements.length > 0 && (
|
||||
<ResultTitle>
|
||||
{t('header.search.result_type.albums', 'Albums')}
|
||||
</ResultTitle>
|
||||
)}
|
||||
{albumElements}
|
||||
{mediaElements.length > 0 && <ResultTitle>Photos</ResultTitle>}
|
||||
{mediaElements.length > 0 && (
|
||||
<ResultTitle>
|
||||
{t('header.search.result_type.photos', 'Photos')}
|
||||
</ResultTitle>
|
||||
)}
|
||||
{mediaElements}
|
||||
</Results>
|
||||
)
|
||||
|
|
|
@ -2,6 +2,7 @@ import React from 'react'
|
|||
import PropTypes from 'prop-types'
|
||||
import { useQuery, gql } from '@apollo/client'
|
||||
import SidebarShare from './Sharing'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
const albumQuery = gql`
|
||||
query getAlbumSidebar($id: ID!) {
|
||||
|
@ -13,16 +14,17 @@ const albumQuery = gql`
|
|||
`
|
||||
|
||||
const AlbumSidebar = ({ albumId }) => {
|
||||
const { t } = useTranslation()
|
||||
const { loading, error, data } = useQuery(albumQuery, {
|
||||
variables: { id: albumId },
|
||||
})
|
||||
|
||||
if (loading) return <div>Loading...</div>
|
||||
if (loading) return <div>{t('general.loading.default', 'Loading...')}</div>
|
||||
if (error) return <div>{error.message}</div>
|
||||
|
||||
return (
|
||||
<div>
|
||||
<p>Album options</p>
|
||||
<p>{t('sidebar.album.title', 'Album options')}</p>
|
||||
<div>
|
||||
<h1>{data.album.title}</h1>
|
||||
<SidebarShare album={data.album} />
|
||||
|
|
|
@ -9,6 +9,7 @@ import SidebarDownload from './SidebarDownload'
|
|||
import SidebarItem from './SidebarItem'
|
||||
import { SidebarFacesOverlay } from '../facesOverlay/FacesOverlay'
|
||||
import { isNil } from '../../helpers/utils'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
const mediaQuery = gql`
|
||||
query sidebarPhoto($id: ID!) {
|
||||
|
@ -123,10 +124,13 @@ const MetadataInfoContainer = styled.div`
|
|||
`
|
||||
|
||||
export const MetadataInfo = ({ media }) => {
|
||||
const { t } = useTranslation()
|
||||
let exifItems = []
|
||||
|
||||
const exifName = exifNameLookup(t)
|
||||
|
||||
if (media?.exif) {
|
||||
let exifKeys = Object.keys(exifNameLookup).filter(
|
||||
let exifKeys = Object.keys(exifName).filter(
|
||||
x => media.exif[x] !== null && x != '__typename'
|
||||
)
|
||||
|
||||
|
@ -146,6 +150,8 @@ export const MetadataInfo = ({ media }) => {
|
|||
exif.exposure = `1/${1 / exif.exposure}`
|
||||
}
|
||||
|
||||
const exposurePrograms = exposureProgramsLookup(t)
|
||||
|
||||
if (
|
||||
!isNil(exif.exposureProgram) &&
|
||||
exposurePrograms[exif.exposureProgram]
|
||||
|
@ -168,7 +174,7 @@ export const MetadataInfo = ({ media }) => {
|
|||
}
|
||||
|
||||
exifItems = exifKeys.map(key => (
|
||||
<SidebarItem key={key} name={exifNameLookup[key]} value={exif[key]} />
|
||||
<SidebarItem key={key} name={exifName[key]} value={exif[key]} />
|
||||
))
|
||||
}
|
||||
|
||||
|
@ -206,62 +212,98 @@ MetadataInfo.propTypes = {
|
|||
media: PropTypes.object,
|
||||
}
|
||||
|
||||
const exifNameLookup = {
|
||||
camera: 'Camera',
|
||||
maker: 'Maker',
|
||||
lens: 'Lens',
|
||||
exposureProgram: 'Program',
|
||||
dateShot: 'Date Shot',
|
||||
exposure: 'Exposure',
|
||||
aperture: 'Aperture',
|
||||
iso: 'ISO',
|
||||
focalLength: 'Focal Length',
|
||||
flash: 'Flash',
|
||||
}
|
||||
const exifNameLookup = t => ({
|
||||
camera: t('sidebar.media.exif.name.camera', 'Camera'),
|
||||
maker: t('sidebar.media.exif.name.maker', 'Maker'),
|
||||
lens: t('sidebar.media.exif.name.lens', 'Lens'),
|
||||
exposureProgram: t('sidebar.media.exif.name.exposure_program', 'Program'),
|
||||
dateShot: t('sidebar.media.exif.name.date_shot', 'Date shot'),
|
||||
exposure: t('sidebar.media.exif.name.exposure', 'Exposure'),
|
||||
aperture: t('sidebar.media.exif.name.aperture', 'Aperture'),
|
||||
iso: t('sidebar.media.exif.name.iso', 'ISO'),
|
||||
focalLength: t('sidebar.media.exif.name.focal_length', 'Focal length'),
|
||||
flash: t('sidebar.media.exif.name.flash', 'Flash'),
|
||||
})
|
||||
|
||||
// From https://exiftool.org/TagNames/EXIF.html
|
||||
const exposurePrograms = {
|
||||
0: 'Not defined',
|
||||
1: 'Manual',
|
||||
2: 'Normal program',
|
||||
3: 'Aperture priority',
|
||||
4: 'Shutter priority',
|
||||
5: 'Creative program',
|
||||
6: 'Action program',
|
||||
7: 'Portrait mode',
|
||||
8: 'Landscape mode ',
|
||||
9: 'Bulb',
|
||||
}
|
||||
const exposureProgramsLookup = t => ({
|
||||
0: t('sidebar.media.exif.exposure_program.not_defined', 'Not defined'),
|
||||
1: t('sidebar.media.exif.exposure_program.manual', 'Manual'),
|
||||
2: t('sidebar.media.exif.exposure_program.normal_program', 'Normal program'),
|
||||
3: t(
|
||||
'sidebar.media.exif.exposure_program.aperture_priority',
|
||||
'Aperture priority'
|
||||
),
|
||||
4: t(
|
||||
'sidebar.media.exif.exposure_program.shutter_priority',
|
||||
'Shutter priority'
|
||||
),
|
||||
5: t(
|
||||
'sidebar.media.exif.exposure_program.creative_program',
|
||||
'Creative program'
|
||||
),
|
||||
6: t('sidebar.media.exif.exposure_program.action_program', 'Action program'),
|
||||
7: t('sidebar.media.exif.exposure_program.portrait_mode', 'Portrait mode'),
|
||||
8: t('sidebar.media.exif.exposure_program.landscape_mode', 'Landscape mode'),
|
||||
9: t('sidebar.media.exif.exposure_program.bulb', 'Bulb'),
|
||||
})
|
||||
|
||||
// From https://exiftool.org/TagNames/EXIF.html#Flash
|
||||
const flash = {
|
||||
0x0: 'No Flash',
|
||||
0x1: 'Fired',
|
||||
0x5: 'Fired, Return not detected',
|
||||
0x7: 'Fired, Return detected',
|
||||
0x8: 'On, Did not fire',
|
||||
0x9: 'On, Fired',
|
||||
0xd: 'On, Return not detected',
|
||||
0xf: 'On, Return detected',
|
||||
0x10: 'Off, Did not fire',
|
||||
0x14: 'Off, Did not fire, Return not detected',
|
||||
0x18: 'Auto, Did not fire',
|
||||
0x19: 'Auto, Fired',
|
||||
0x1d: 'Auto, Fired, Return not detected',
|
||||
0x1f: 'Auto, Fired, Return detected',
|
||||
0x20: 'No flash function',
|
||||
0x30: 'Off, No flash function',
|
||||
0x41: 'Fired, Red-eye reduction',
|
||||
0x45: 'Fired, Red-eye reduction, Return not detected',
|
||||
0x47: 'Fired, Red-eye reduction, Return detected',
|
||||
0x49: 'On, Red-eye reduction',
|
||||
0x4d: 'On, Red-eye reduction, Return not detected',
|
||||
0x4f: 'On, Red-eye reduction, Return detected',
|
||||
0x50: 'Off, Red-eye reduction',
|
||||
0x58: 'Auto, Did not fire, Red-eye reduction',
|
||||
0x59: 'Auto, Fired, Red-eye reduction',
|
||||
0x5d: 'Auto, Fired, Red-eye reduction, Return not detected',
|
||||
0x5f: 'Auto, Fired, Red-eye reduction, Return detected',
|
||||
const flash = t => {
|
||||
const values = {
|
||||
no_flash: t('sidebar.media.exif.flash.no_flash', 'No Flash'),
|
||||
fired: t('sidebar.media.exif.flash.fired', 'Fired'),
|
||||
did_not_fire: t('sidebar.media.exif.flash.did_not_fire', 'Did not fire'),
|
||||
on: t('sidebar.media.exif.flash.on', 'On'),
|
||||
off: t('sidebar.media.exif.flash.off', 'Off'),
|
||||
auto: t('sidebar.media.exif.flash.auto', 'Auto'),
|
||||
return_not_detected: t(
|
||||
'sidebar.media.exif.flash.return_not_detected',
|
||||
'Return not detected'
|
||||
),
|
||||
return_detected: t(
|
||||
'sidebar.media.exif.flash.return_detected',
|
||||
'Return detected'
|
||||
),
|
||||
no_flash_function: t(
|
||||
'sidebar.media.exif.flash.no_flash_function',
|
||||
'No flash function'
|
||||
),
|
||||
red_eye_reduction: t(
|
||||
'sidebar.media.exif.flash.red_eye_reduction',
|
||||
'Red-eye reduction'
|
||||
),
|
||||
}
|
||||
|
||||
return {
|
||||
0x0: values['no_flash'],
|
||||
0x1: values['fired'],
|
||||
0x5: `${values['fired']}, ${values['return_not_detected']}`,
|
||||
0x7: `${values['fired']}, ${values['return_detected']}`,
|
||||
0x8: `${values['on']}, ${values['did_not_fire']}`,
|
||||
0x9: `${values['on']}, ${values['fired']}`,
|
||||
0xd: `${values['on']}, ${values['return_not_detected']}`,
|
||||
0xf: `${values['on']}, ${values['return_detected']}`,
|
||||
0x10: `${values['off']}, ${values['did_not_fire']}`,
|
||||
0x14: `${values['off']}, ${values['did_not_fire']}, ${values['return_not_detected']}`,
|
||||
0x18: `${values['auto']}, ${values['did_not_fire']}`,
|
||||
0x19: `${values['auto']}, ${values['fired']}`,
|
||||
0x1d: `${values['auto']}, ${values['fired']}, ${values['return_not_detected']}`,
|
||||
0x1f: `${values['auto']}, ${values['fired']}, ${values['return_detected']}`,
|
||||
0x20: `${values['no_flash_function']}`,
|
||||
0x30: `${values['off']}, ${values['no_flash_function']}`,
|
||||
0x41: `${values['fired']}, ${values['red_eye_reduction']}`,
|
||||
0x45: `${values['fired']}, ${values['red_eye_reduction']}, ${values['return_not_detected']}`,
|
||||
0x47: `${values['fired']}, ${values['red_eye_reduction']}, ${values['return_detected']}`,
|
||||
0x49: `${values['on']}, ${values['red_eye_reduction']}`,
|
||||
0x4d: `${values['on']}, ${values['red_eye_reduction']}, ${values['return_not_detected']}`,
|
||||
0x4f: `${values['on']}, ${values['red_eye_reduction']}, ${values['return_detected']}`,
|
||||
0x50: `${values['off']}, ${values['red_eye_reduction']}`,
|
||||
0x58: `${values['auto']}, ${values['did_not_fire']}, ${values['red_eye_reduction']}`,
|
||||
0x59: `${values['auto']}, ${values['fired']}, ${values['red_eye_reduction']}`,
|
||||
0x5d: `${values['auto']}, ${values['red_eye_reduction']}, ${values['return_not_detected']}`,
|
||||
0x5f: `${values['auto']}, ${values['red_eye_redcution']}, ${values['return_detected']}`,
|
||||
}
|
||||
}
|
||||
|
||||
// From https://exiftool.org/TagNames/EXIF.html
|
||||
|
|
|
@ -12,6 +12,7 @@ import {
|
|||
import copy from 'copy-to-clipboard'
|
||||
import { authToken } from '../../helpers/authentication'
|
||||
import styled from 'styled-components'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
const sharePhotoQuery = gql`
|
||||
query sidbarGetPhotoShares($id: ID!) {
|
||||
|
@ -73,6 +74,7 @@ const deleteShareMutation = gql`
|
|||
`
|
||||
|
||||
const ShareItemMoreDropdown = ({ id, share, isPhoto }) => {
|
||||
const { t } = useTranslation()
|
||||
const query = isPhoto ? sharePhotoQuery : shareAlbumQuery
|
||||
|
||||
const [deleteShare, { loading: deleteShareLoading }] = useMutation(
|
||||
|
@ -187,7 +189,7 @@ const ShareItemMoreDropdown = ({ id, share, isPhoto }) => {
|
|||
}}
|
||||
>
|
||||
<Checkbox
|
||||
label="Password"
|
||||
label={t('login_page.field.password', 'Password')}
|
||||
onClick={e => e.stopPropagation()}
|
||||
checked={showPasswordInput}
|
||||
onChange={() => {
|
||||
|
@ -197,7 +199,7 @@ const ShareItemMoreDropdown = ({ id, share, isPhoto }) => {
|
|||
{addPasswordInput}
|
||||
</Dropdown.Item>
|
||||
<Dropdown.Item
|
||||
text="Delete"
|
||||
text={t('general.action.delete', 'Delete')}
|
||||
icon="delete"
|
||||
disabled={deleteShareLoading}
|
||||
onClick={() => {
|
||||
|
@ -224,6 +226,7 @@ const ShareButtonGroup = styled(Button.Group)`
|
|||
`
|
||||
|
||||
const SidebarShare = ({ photo, album }) => {
|
||||
const { t } = useTranslation()
|
||||
if ((!photo || !photo.id) && (!album || !album.id)) return null
|
||||
if (!authToken()) return null
|
||||
|
||||
|
@ -257,7 +260,7 @@ const SidebarShare = ({ photo, album }) => {
|
|||
}
|
||||
|
||||
if (!content && sharesLoading) {
|
||||
content = <div>Loading shares...</div>
|
||||
content = <div>{t('general.loading.shares', 'Loading shares...')}</div>
|
||||
}
|
||||
|
||||
if (!content) {
|
||||
|
@ -266,13 +269,13 @@ const SidebarShare = ({ photo, album }) => {
|
|||
const optionsRows = shares.map(share => (
|
||||
<Table.Row key={share.token}>
|
||||
<Table.Cell>
|
||||
<b>Public Link</b> {share.token}
|
||||
<b>{t('sidebar.sharing.public_link', 'Public Link')}</b> {share.token}
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
<ShareButtonGroup>
|
||||
<Button
|
||||
icon="chain"
|
||||
content="Copy link"
|
||||
content={t('sidebar.sharing.copy_link', 'Copy Link')}
|
||||
onClick={() => {
|
||||
copy(`${location.origin}/share/${share.token}`)
|
||||
}}
|
||||
|
@ -286,7 +289,9 @@ const SidebarShare = ({ photo, album }) => {
|
|||
if (optionsRows.length == 0) {
|
||||
optionsRows.push(
|
||||
<Table.Row key="no-shares">
|
||||
<Table.Cell colSpan="2">No shares found</Table.Cell>
|
||||
<Table.Cell colSpan="2">
|
||||
{t('sidebar.sharing.no_shares_found', 'No shares found')}
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
)
|
||||
}
|
||||
|
@ -296,7 +301,9 @@ const SidebarShare = ({ photo, album }) => {
|
|||
<Table>
|
||||
<Table.Header>
|
||||
<Table.Row>
|
||||
<Table.HeaderCell colSpan="2">Public Shares</Table.HeaderCell>
|
||||
<Table.HeaderCell colSpan="2">
|
||||
{t('sidebar.sharing.table_header', 'Public shares')}
|
||||
</Table.HeaderCell>
|
||||
</Table.Row>
|
||||
</Table.Header>
|
||||
<Table.Body>{optionsRows}</Table.Body>
|
||||
|
@ -304,7 +311,7 @@ const SidebarShare = ({ photo, album }) => {
|
|||
<Table.Row>
|
||||
<Table.HeaderCell colSpan="2">
|
||||
<Button
|
||||
content="Add share"
|
||||
content={t('sidebar.sharing.add_share', 'Add shares')}
|
||||
icon="add"
|
||||
floated="right"
|
||||
positive
|
||||
|
@ -328,7 +335,7 @@ const SidebarShare = ({ photo, album }) => {
|
|||
|
||||
return (
|
||||
<div>
|
||||
<h2>Sharing options</h2>
|
||||
<h2>{t('sidebar.sharing.title', 'Sharing options')}</h2>
|
||||
{content}
|
||||
</div>
|
||||
)
|
||||
|
|
|
@ -5,6 +5,7 @@ import styled from 'styled-components'
|
|||
import { MessageState } from '../messages/Messages'
|
||||
import { useLazyQuery, gql } from '@apollo/client'
|
||||
import { authToken } from '../../helpers/authentication'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
export const SIDEBAR_DOWNLOAD_QUERY = gql`
|
||||
query sidebarDownloadQuery($mediaId: ID!) {
|
||||
|
@ -23,14 +24,29 @@ export const SIDEBAR_DOWNLOAD_QUERY = gql`
|
|||
}
|
||||
`
|
||||
|
||||
function formatBytes(bytes) {
|
||||
var sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB']
|
||||
const formatBytes = t => bytes => {
|
||||
if (bytes == 0) return '0 Byte'
|
||||
var i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024)))
|
||||
return Math.round(bytes / Math.pow(1024, i), 2) + ' ' + sizes[i]
|
||||
const i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024)))
|
||||
const count = Math.round(bytes / Math.pow(1024, i), 2)
|
||||
|
||||
switch (i) {
|
||||
case 0:
|
||||
// i18next-extract-mark-plural-next-line
|
||||
return t('sidebar.download.filesize.byte', '{{count}} Byte', { count })
|
||||
case 1:
|
||||
return t('sidebar.download.filesize.kilo_byte', '{{count}} KB', { count })
|
||||
case 2:
|
||||
return t('sidebar.download.filesize.mega_byte', '{{count}} MB', { count })
|
||||
case 3:
|
||||
return t('sidebar.download.filesize.giga_byte', '{{count}} GB', { count })
|
||||
case 4:
|
||||
return t('sidebar.download.filesize.tera_byte', '{{count}} TB', { count })
|
||||
default:
|
||||
return new Error(`invalid byte value: ${bytes}`)
|
||||
}
|
||||
}
|
||||
|
||||
const downloadMedia = async url => {
|
||||
const downloadMedia = t => async url => {
|
||||
const imgUrl = new URL(url, location.origin)
|
||||
|
||||
if (authToken() == null) {
|
||||
|
@ -47,7 +63,7 @@ const downloadMedia = async url => {
|
|||
|
||||
let blob = null
|
||||
if (response.headers.has('content-length')) {
|
||||
blob = await downloadMediaShowProgress(response)
|
||||
blob = await downloadMediaShowProgress(t)(response)
|
||||
} else {
|
||||
blob = await response.blob()
|
||||
}
|
||||
|
@ -57,7 +73,7 @@ const downloadMedia = async url => {
|
|||
downloadBlob(blob, filename)
|
||||
}
|
||||
|
||||
const downloadMediaShowProgress = async response => {
|
||||
const downloadMediaShowProgress = t => async response => {
|
||||
const totalBytes = Number(response.headers.get('content-length'))
|
||||
const reader = response.body.getReader()
|
||||
let data = new Uint8Array(totalBytes)
|
||||
|
@ -98,7 +114,7 @@ const downloadMediaShowProgress = async response => {
|
|||
props: {
|
||||
header: 'Downloading photo',
|
||||
percent: (receivedBytes / totalBytes) * 100,
|
||||
content: `${formatBytes(receivedBytes)} of ${formatBytes(
|
||||
content: `${formatBytes(t)(receivedBytes)} of ${formatBytes(t)(
|
||||
totalBytes
|
||||
)} bytes downloaded`,
|
||||
},
|
||||
|
@ -151,6 +167,7 @@ const DownloadTableRow = styled(Table.Row)`
|
|||
`
|
||||
|
||||
const SidebarDownload = ({ photo }) => {
|
||||
const { t } = useTranslation()
|
||||
if (!photo || !photo.id) return null
|
||||
|
||||
const [
|
||||
|
@ -176,29 +193,39 @@ const SidebarDownload = ({ photo }) => {
|
|||
return url.split(/[#?]/)[0].split('.').pop().trim().toLowerCase()
|
||||
}
|
||||
|
||||
const download = downloadMedia(t)
|
||||
const bytes = formatBytes(t)
|
||||
let downloadRows = downloads.map(x => (
|
||||
<DownloadTableRow
|
||||
key={x.mediaUrl.url}
|
||||
onClick={() => downloadMedia(x.mediaUrl.url)}
|
||||
onClick={() => download(x.mediaUrl.url)}
|
||||
>
|
||||
<Table.Cell>{`${x.title}`}</Table.Cell>
|
||||
<Table.Cell>{`${x.mediaUrl.width} x ${x.mediaUrl.height}`}</Table.Cell>
|
||||
<Table.Cell>{`${formatBytes(x.mediaUrl.fileSize)}`}</Table.Cell>
|
||||
<Table.Cell>{`${bytes(x.mediaUrl.fileSize)}`}</Table.Cell>
|
||||
<Table.Cell>{extractExtension(x.mediaUrl.url)}</Table.Cell>
|
||||
</DownloadTableRow>
|
||||
))
|
||||
|
||||
return (
|
||||
<div style={{ marginBottom: 24 }}>
|
||||
<h2>Download</h2>
|
||||
<h2>{t('sidebar.download.title', 'Download')}</h2>
|
||||
|
||||
<Table selectable singleLine compact>
|
||||
<Table.Header>
|
||||
<Table.Row>
|
||||
<Table.HeaderCell>Name</Table.HeaderCell>
|
||||
<Table.HeaderCell>Dimensions</Table.HeaderCell>
|
||||
<Table.HeaderCell>Size</Table.HeaderCell>
|
||||
<Table.HeaderCell>Type</Table.HeaderCell>
|
||||
<Table.HeaderCell>
|
||||
{t('sidebar.download.table_columns.name', 'Name')}
|
||||
</Table.HeaderCell>
|
||||
<Table.HeaderCell>
|
||||
{t('sidebar.download.table_columns.dimensions', 'Dimensions')}
|
||||
</Table.HeaderCell>
|
||||
<Table.HeaderCell>
|
||||
{t('sidebar.download.table_columns.file_size', 'Size')}
|
||||
</Table.HeaderCell>
|
||||
<Table.HeaderCell>
|
||||
{t('sidebar.download.table_columns.file_type', 'Type')}
|
||||
</Table.HeaderCell>
|
||||
</Table.Row>
|
||||
</Table.Header>
|
||||
<Table.Body>{downloadRows}</Table.Body>
|
||||
|
|
Loading…
Reference in New Issue