Make people page translatable
This commit is contained in:
parent
e2f6bdb365
commit
360db25ec3
|
@ -68,10 +68,58 @@
|
|||
},
|
||||
"people_page": {
|
||||
"face_group": {
|
||||
"label_placeholder": "Navn",
|
||||
"unlabeled": "Ikke navngivet"
|
||||
"action": {
|
||||
"add_label": null,
|
||||
"change_label": null,
|
||||
"detach_face": null,
|
||||
"merge_face": null,
|
||||
"move_faces": null
|
||||
},
|
||||
"recognize_unlabeled_faces_button": "Genkend ikke navngivede ansigter"
|
||||
"label_placeholder": "Navn",
|
||||
"unlabeled": "Ikke navngivet",
|
||||
"unlabeled_person": null
|
||||
},
|
||||
"modal": {
|
||||
"action": {
|
||||
"merge": null
|
||||
},
|
||||
"detach_image_faces": {
|
||||
"action": {
|
||||
"detach": null,
|
||||
"select_images": null
|
||||
},
|
||||
"description": null,
|
||||
"title": null
|
||||
},
|
||||
"merge_face_groups": {
|
||||
"description": null,
|
||||
"destination_table": {
|
||||
"title": null
|
||||
},
|
||||
"title": null
|
||||
},
|
||||
"move_image_faces": {
|
||||
"description": null,
|
||||
"destination_face_group_table": {
|
||||
"move_action": null,
|
||||
"title": null
|
||||
},
|
||||
"image_select_table": {
|
||||
"next_action": null,
|
||||
"title": null
|
||||
},
|
||||
"title": null
|
||||
}
|
||||
},
|
||||
"recognize_unlabeled_faces_button": "Genkend ikke navngivede ansigter",
|
||||
"table": {
|
||||
"select_face_group": {
|
||||
"search_faces_placeholder": null
|
||||
},
|
||||
"select_image_faces": {
|
||||
"search_images_placeholder": null
|
||||
}
|
||||
}
|
||||
},
|
||||
"photos_page": {
|
||||
"title": "Billeder"
|
||||
|
|
|
@ -68,10 +68,58 @@
|
|||
},
|
||||
"people_page": {
|
||||
"face_group": {
|
||||
"label_placeholder": "Zuordnung",
|
||||
"unlabeled": "Nicht zugeordnet"
|
||||
"action": {
|
||||
"add_label": null,
|
||||
"change_label": null,
|
||||
"detach_face": null,
|
||||
"merge_face": null,
|
||||
"move_faces": null
|
||||
},
|
||||
"recognize_unlabeled_faces_button": "Nicht zugeordnete Gesichter erkennen"
|
||||
"label_placeholder": "Zuordnung",
|
||||
"unlabeled": "Nicht zugeordnet",
|
||||
"unlabeled_person": null
|
||||
},
|
||||
"modal": {
|
||||
"action": {
|
||||
"merge": null
|
||||
},
|
||||
"detach_image_faces": {
|
||||
"action": {
|
||||
"detach": null,
|
||||
"select_images": null
|
||||
},
|
||||
"description": null,
|
||||
"title": null
|
||||
},
|
||||
"merge_face_groups": {
|
||||
"description": null,
|
||||
"destination_table": {
|
||||
"title": null
|
||||
},
|
||||
"title": null
|
||||
},
|
||||
"move_image_faces": {
|
||||
"description": null,
|
||||
"destination_face_group_table": {
|
||||
"move_action": null,
|
||||
"title": null
|
||||
},
|
||||
"image_select_table": {
|
||||
"next_action": null,
|
||||
"title": null
|
||||
},
|
||||
"title": null
|
||||
}
|
||||
},
|
||||
"recognize_unlabeled_faces_button": "Nicht zugeordnete Gesichter erkennen",
|
||||
"table": {
|
||||
"select_face_group": {
|
||||
"search_faces_placeholder": null
|
||||
},
|
||||
"select_image_faces": {
|
||||
"search_images_placeholder": null
|
||||
}
|
||||
}
|
||||
},
|
||||
"photos_page": {
|
||||
"title": "Fotos"
|
||||
|
|
|
@ -68,10 +68,58 @@
|
|||
},
|
||||
"people_page": {
|
||||
"face_group": {
|
||||
"label_placeholder": "Label",
|
||||
"unlabeled": "Unlabeled"
|
||||
"action": {
|
||||
"add_label": "Add Label",
|
||||
"change_label": "Change Label",
|
||||
"detach_face": "Detach Face",
|
||||
"merge_face": "Merge Face",
|
||||
"move_faces": "Move Faces"
|
||||
},
|
||||
"recognize_unlabeled_faces_button": "Recognize unlabeled faces"
|
||||
"label_placeholder": "Label",
|
||||
"unlabeled": "Unlabeled",
|
||||
"unlabeled_person": "Unlabeled person"
|
||||
},
|
||||
"modal": {
|
||||
"action": {
|
||||
"merge": "Merge"
|
||||
},
|
||||
"detach_image_faces": {
|
||||
"action": {
|
||||
"detach": "Detach image faces",
|
||||
"select_images": "Select images to detach"
|
||||
},
|
||||
"description": "Detach selected images of this face group and move them to a new face groups",
|
||||
"title": "Detach Image Faces"
|
||||
},
|
||||
"merge_face_groups": {
|
||||
"description": "All images within this face group will be merged into the selected face group.",
|
||||
"destination_table": {
|
||||
"title": "Select the destination face"
|
||||
},
|
||||
"title": "Merge Face Groups"
|
||||
},
|
||||
"move_image_faces": {
|
||||
"description": "Move selected images of this face group to another face group",
|
||||
"destination_face_group_table": {
|
||||
"move_action": "Move image faces",
|
||||
"title": "Select destination face group"
|
||||
},
|
||||
"image_select_table": {
|
||||
"next_action": "Next",
|
||||
"title": "Select images to move"
|
||||
},
|
||||
"title": "Move Image Faces"
|
||||
}
|
||||
},
|
||||
"recognize_unlabeled_faces_button": "Recognize unlabeled faces",
|
||||
"table": {
|
||||
"select_face_group": {
|
||||
"search_faces_placeholder": "Search faces..."
|
||||
},
|
||||
"select_image_faces": {
|
||||
"search_images_placeholder": "Search images..."
|
||||
}
|
||||
}
|
||||
},
|
||||
"photos_page": {
|
||||
"title": "Photos"
|
||||
|
|
|
@ -68,10 +68,58 @@
|
|||
},
|
||||
"people_page": {
|
||||
"face_group": {
|
||||
"label_placeholder": "Etiqueta",
|
||||
"unlabeled": "Sin etiquetar"
|
||||
"action": {
|
||||
"add_label": null,
|
||||
"change_label": null,
|
||||
"detach_face": null,
|
||||
"merge_face": null,
|
||||
"move_faces": null
|
||||
},
|
||||
"recognize_unlabeled_faces_button": "Reconocer caras sin etiquetar"
|
||||
"label_placeholder": "Etiqueta",
|
||||
"unlabeled": "Sin etiquetar",
|
||||
"unlabeled_person": null
|
||||
},
|
||||
"modal": {
|
||||
"action": {
|
||||
"merge": null
|
||||
},
|
||||
"detach_image_faces": {
|
||||
"action": {
|
||||
"detach": null,
|
||||
"select_images": null
|
||||
},
|
||||
"description": null,
|
||||
"title": null
|
||||
},
|
||||
"merge_face_groups": {
|
||||
"description": null,
|
||||
"destination_table": {
|
||||
"title": null
|
||||
},
|
||||
"title": null
|
||||
},
|
||||
"move_image_faces": {
|
||||
"description": null,
|
||||
"destination_face_group_table": {
|
||||
"move_action": null,
|
||||
"title": null
|
||||
},
|
||||
"image_select_table": {
|
||||
"next_action": null,
|
||||
"title": null
|
||||
},
|
||||
"title": null
|
||||
}
|
||||
},
|
||||
"recognize_unlabeled_faces_button": "Reconocer caras sin etiquetar",
|
||||
"table": {
|
||||
"select_face_group": {
|
||||
"search_faces_placeholder": null
|
||||
},
|
||||
"select_image_faces": {
|
||||
"search_images_placeholder": null
|
||||
}
|
||||
}
|
||||
},
|
||||
"photos_page": {
|
||||
"title": "Fotos"
|
||||
|
|
|
@ -68,10 +68,58 @@
|
|||
},
|
||||
"people_page": {
|
||||
"face_group": {
|
||||
"label_placeholder": "Étiquette",
|
||||
"unlabeled": "Sans étiquette"
|
||||
"action": {
|
||||
"add_label": null,
|
||||
"change_label": null,
|
||||
"detach_face": null,
|
||||
"merge_face": null,
|
||||
"move_faces": null
|
||||
},
|
||||
"recognize_unlabeled_faces_button": "Reconnaître les visages sans étiquette"
|
||||
"label_placeholder": "Étiquette",
|
||||
"unlabeled": "Sans étiquette",
|
||||
"unlabeled_person": null
|
||||
},
|
||||
"modal": {
|
||||
"action": {
|
||||
"merge": null
|
||||
},
|
||||
"detach_image_faces": {
|
||||
"action": {
|
||||
"detach": null,
|
||||
"select_images": null
|
||||
},
|
||||
"description": null,
|
||||
"title": null
|
||||
},
|
||||
"merge_face_groups": {
|
||||
"description": null,
|
||||
"destination_table": {
|
||||
"title": null
|
||||
},
|
||||
"title": null
|
||||
},
|
||||
"move_image_faces": {
|
||||
"description": null,
|
||||
"destination_face_group_table": {
|
||||
"move_action": null,
|
||||
"title": null
|
||||
},
|
||||
"image_select_table": {
|
||||
"next_action": null,
|
||||
"title": null
|
||||
},
|
||||
"title": null
|
||||
}
|
||||
},
|
||||
"recognize_unlabeled_faces_button": "Reconnaître les visages sans étiquette",
|
||||
"table": {
|
||||
"select_face_group": {
|
||||
"search_faces_placeholder": null
|
||||
},
|
||||
"select_image_faces": {
|
||||
"search_images_placeholder": null
|
||||
}
|
||||
}
|
||||
},
|
||||
"photos_page": {
|
||||
"title": "Photos"
|
||||
|
|
|
@ -68,10 +68,58 @@
|
|||
},
|
||||
"people_page": {
|
||||
"face_group": {
|
||||
"label_placeholder": "Etichetta",
|
||||
"unlabeled": "Senza etichetta"
|
||||
"action": {
|
||||
"add_label": null,
|
||||
"change_label": null,
|
||||
"detach_face": null,
|
||||
"merge_face": null,
|
||||
"move_faces": null
|
||||
},
|
||||
"recognize_unlabeled_faces_button": "Identifica facce senza etichetta"
|
||||
"label_placeholder": "Etichetta",
|
||||
"unlabeled": "Senza etichetta",
|
||||
"unlabeled_person": null
|
||||
},
|
||||
"modal": {
|
||||
"action": {
|
||||
"merge": null
|
||||
},
|
||||
"detach_image_faces": {
|
||||
"action": {
|
||||
"detach": null,
|
||||
"select_images": null
|
||||
},
|
||||
"description": null,
|
||||
"title": null
|
||||
},
|
||||
"merge_face_groups": {
|
||||
"description": null,
|
||||
"destination_table": {
|
||||
"title": null
|
||||
},
|
||||
"title": null
|
||||
},
|
||||
"move_image_faces": {
|
||||
"description": null,
|
||||
"destination_face_group_table": {
|
||||
"move_action": null,
|
||||
"title": null
|
||||
},
|
||||
"image_select_table": {
|
||||
"next_action": null,
|
||||
"title": null
|
||||
},
|
||||
"title": null
|
||||
}
|
||||
},
|
||||
"recognize_unlabeled_faces_button": "Identifica facce senza etichetta",
|
||||
"table": {
|
||||
"select_face_group": {
|
||||
"search_faces_placeholder": null
|
||||
},
|
||||
"select_image_faces": {
|
||||
"search_images_placeholder": null
|
||||
}
|
||||
}
|
||||
},
|
||||
"photos_page": {
|
||||
"title": "Foto"
|
||||
|
|
|
@ -68,10 +68,58 @@
|
|||
},
|
||||
"people_page": {
|
||||
"face_group": {
|
||||
"label_placeholder": "Etykieta",
|
||||
"unlabeled": "Nieoznakowany"
|
||||
"action": {
|
||||
"add_label": null,
|
||||
"change_label": null,
|
||||
"detach_face": null,
|
||||
"merge_face": null,
|
||||
"move_faces": null
|
||||
},
|
||||
"recognize_unlabeled_faces_button": "Rozpoznaj nieoznakowane twarze"
|
||||
"label_placeholder": "Etykieta",
|
||||
"unlabeled": "Nieoznakowany",
|
||||
"unlabeled_person": null
|
||||
},
|
||||
"modal": {
|
||||
"action": {
|
||||
"merge": null
|
||||
},
|
||||
"detach_image_faces": {
|
||||
"action": {
|
||||
"detach": null,
|
||||
"select_images": null
|
||||
},
|
||||
"description": null,
|
||||
"title": null
|
||||
},
|
||||
"merge_face_groups": {
|
||||
"description": null,
|
||||
"destination_table": {
|
||||
"title": null
|
||||
},
|
||||
"title": null
|
||||
},
|
||||
"move_image_faces": {
|
||||
"description": null,
|
||||
"destination_face_group_table": {
|
||||
"move_action": null,
|
||||
"title": null
|
||||
},
|
||||
"image_select_table": {
|
||||
"next_action": null,
|
||||
"title": null
|
||||
},
|
||||
"title": null
|
||||
}
|
||||
},
|
||||
"recognize_unlabeled_faces_button": "Rozpoznaj nieoznakowane twarze",
|
||||
"table": {
|
||||
"select_face_group": {
|
||||
"search_faces_placeholder": null
|
||||
},
|
||||
"select_image_faces": {
|
||||
"search_images_placeholder": null
|
||||
}
|
||||
}
|
||||
},
|
||||
"photos_page": {
|
||||
"title": "Zdjęcia"
|
||||
|
|
|
@ -68,10 +68,58 @@
|
|||
},
|
||||
"people_page": {
|
||||
"face_group": {
|
||||
"label_placeholder": "Märkning",
|
||||
"unlabeled": "Omärkt"
|
||||
"action": {
|
||||
"add_label": null,
|
||||
"change_label": null,
|
||||
"detach_face": null,
|
||||
"merge_face": null,
|
||||
"move_faces": null
|
||||
},
|
||||
"recognize_unlabeled_faces_button": "Känna igen omärkta ansikten"
|
||||
"label_placeholder": "Märkning",
|
||||
"unlabeled": "Omärkt",
|
||||
"unlabeled_person": null
|
||||
},
|
||||
"modal": {
|
||||
"action": {
|
||||
"merge": null
|
||||
},
|
||||
"detach_image_faces": {
|
||||
"action": {
|
||||
"detach": null,
|
||||
"select_images": null
|
||||
},
|
||||
"description": null,
|
||||
"title": null
|
||||
},
|
||||
"merge_face_groups": {
|
||||
"description": null,
|
||||
"destination_table": {
|
||||
"title": null
|
||||
},
|
||||
"title": null
|
||||
},
|
||||
"move_image_faces": {
|
||||
"description": null,
|
||||
"destination_face_group_table": {
|
||||
"move_action": null,
|
||||
"title": null
|
||||
},
|
||||
"image_select_table": {
|
||||
"next_action": null,
|
||||
"title": null
|
||||
},
|
||||
"title": null
|
||||
}
|
||||
},
|
||||
"recognize_unlabeled_faces_button": "Känna igen omärkta ansikten",
|
||||
"table": {
|
||||
"select_face_group": {
|
||||
"search_faces_placeholder": null
|
||||
},
|
||||
"select_image_faces": {
|
||||
"search_images_placeholder": null
|
||||
}
|
||||
}
|
||||
},
|
||||
"photos_page": {
|
||||
"title": "Bilder"
|
||||
|
|
|
@ -1,10 +1,23 @@
|
|||
import { gql, useMutation } from '@apollo/client'
|
||||
import PropTypes from 'prop-types'
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useHistory } from 'react-router-dom'
|
||||
import { Button, Modal } from 'semantic-ui-react'
|
||||
import { isNil } from '../../../helpers/utils'
|
||||
import { MY_FACES_QUERY } from '../PeoplePage'
|
||||
import {
|
||||
myFaces_myFaceGroups,
|
||||
myFaces_myFaceGroups_imageFaces,
|
||||
} from '../__generated__/myFaces'
|
||||
import SelectImageFacesTable from './SelectImageFacesTable'
|
||||
import {
|
||||
detachImageFaces,
|
||||
detachImageFacesVariables,
|
||||
} from './__generated__/detachImageFaces'
|
||||
import {
|
||||
singleFaceGroup_faceGroup,
|
||||
singleFaceGroup_faceGroup_imageFaces,
|
||||
} from './__generated__/singleFaceGroup'
|
||||
|
||||
const DETACH_IMAGE_FACES_MUTATION = gql`
|
||||
mutation detachImageFaces($faceIDs: [ID!]!) {
|
||||
|
@ -15,12 +28,28 @@ const DETACH_IMAGE_FACES_MUTATION = gql`
|
|||
}
|
||||
`
|
||||
|
||||
const DetachImageFacesModal = ({ open, setOpen, faceGroup }) => {
|
||||
const [selectedImageFaces, setSelectedImageFaces] = useState([])
|
||||
let history = useHistory()
|
||||
type DetachImageFacesModalProps = {
|
||||
open: boolean
|
||||
setOpen(open: boolean): void
|
||||
faceGroup: myFaces_myFaceGroups | singleFaceGroup_faceGroup
|
||||
}
|
||||
|
||||
const [detachImageFacesMutation] = useMutation(DETACH_IMAGE_FACES_MUTATION, {
|
||||
variables: {},
|
||||
const DetachImageFacesModal = ({
|
||||
open,
|
||||
setOpen,
|
||||
faceGroup,
|
||||
}: DetachImageFacesModalProps) => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
const [selectedImageFaces, setSelectedImageFaces] = useState<
|
||||
(myFaces_myFaceGroups_imageFaces | singleFaceGroup_faceGroup_imageFaces)[]
|
||||
>([])
|
||||
const history = useHistory()
|
||||
|
||||
const [detachImageFacesMutation] = useMutation<
|
||||
detachImageFaces,
|
||||
detachImageFacesVariables
|
||||
>(DETACH_IMAGE_FACES_MUTATION, {
|
||||
refetchQueries: [
|
||||
{
|
||||
query: MY_FACES_QUERY,
|
||||
|
@ -44,6 +73,7 @@ const DetachImageFacesModal = ({ open, setOpen, faceGroup }) => {
|
|||
faceIDs,
|
||||
},
|
||||
}).then(({ data }) => {
|
||||
if (isNil(data)) throw new Error('Expected data not to be null')
|
||||
setOpen(false)
|
||||
history.push(`/people/${data.detachImageFaces.id}`)
|
||||
})
|
||||
|
@ -57,18 +87,25 @@ const DetachImageFacesModal = ({ open, setOpen, faceGroup }) => {
|
|||
onOpen={() => setOpen(true)}
|
||||
open={open}
|
||||
>
|
||||
<Modal.Header>Detach Image Faces</Modal.Header>
|
||||
<Modal.Header>
|
||||
{t('people_page.modal.detach_image_faces.title', 'Detach Image Faces')}
|
||||
</Modal.Header>
|
||||
<Modal.Content scrolling>
|
||||
<Modal.Description>
|
||||
<p>
|
||||
Detach selected images of this face group and move them to a new
|
||||
face group
|
||||
{t(
|
||||
'people_page.modal.detach_image_faces.description',
|
||||
'Detach selected images of this face group and move them to a new face groups'
|
||||
)}
|
||||
</p>
|
||||
<SelectImageFacesTable
|
||||
imageFaces={imageFaces}
|
||||
selectedImageFaces={selectedImageFaces}
|
||||
setSelectedImageFaces={setSelectedImageFaces}
|
||||
title="Select images to detach"
|
||||
title={t(
|
||||
'people_page.modal.detach_image_faces.action.select_images',
|
||||
'Select images to detach'
|
||||
)}
|
||||
/>
|
||||
</Modal.Description>
|
||||
</Modal.Content>
|
||||
|
@ -76,7 +113,10 @@ const DetachImageFacesModal = ({ open, setOpen, faceGroup }) => {
|
|||
<Button onClick={() => setOpen(false)}>Cancel</Button>
|
||||
<Button
|
||||
disabled={selectedImageFaces.length == 0}
|
||||
content="Detach image faces"
|
||||
content={t(
|
||||
'people_page.modal.detach_image_faces.action.detach',
|
||||
'Detach image faces'
|
||||
)}
|
||||
labelPosition="right"
|
||||
icon="checkmark"
|
||||
onClick={() => detachImageFaces()}
|
||||
|
@ -87,10 +127,4 @@ const DetachImageFacesModal = ({ open, setOpen, faceGroup }) => {
|
|||
)
|
||||
}
|
||||
|
||||
DetachImageFacesModal.propTypes = {
|
||||
open: PropTypes.bool.isRequired,
|
||||
setOpen: PropTypes.func.isRequired,
|
||||
faceGroup: PropTypes.object,
|
||||
}
|
||||
|
||||
export default DetachImageFacesModal
|
|
@ -1,5 +1,6 @@
|
|||
import { useMutation } from '@apollo/client'
|
||||
import React, { useState, useEffect, createRef } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Dropdown, Input } from 'semantic-ui-react'
|
||||
import styled from 'styled-components'
|
||||
import { isNil } from '../../../helpers/utils'
|
||||
|
@ -38,6 +39,8 @@ type FaceGroupTitleProps = {
|
|||
}
|
||||
|
||||
const FaceGroupTitle = ({ faceGroup }: FaceGroupTitleProps) => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
const [editLabel, setEditLabel] = useState(false)
|
||||
const [inputValue, setInputValue] = useState(faceGroup?.label ?? '')
|
||||
const inputRef = createRef<Input>()
|
||||
|
@ -91,7 +94,8 @@ const FaceGroupTitle = ({ faceGroup }: FaceGroupTitleProps) => {
|
|||
title = (
|
||||
<TitleWrapper>
|
||||
<TitleLabel labeled={!!faceGroup?.label}>
|
||||
{faceGroup?.label ?? 'Unlabeled person'}
|
||||
{faceGroup?.label ??
|
||||
t('people_page.face_group.unlabeled_person', 'Unlabeled person')}
|
||||
</TitleLabel>
|
||||
<TitleDropdown
|
||||
icon={{
|
||||
|
@ -102,22 +106,32 @@ const FaceGroupTitle = ({ faceGroup }: FaceGroupTitleProps) => {
|
|||
<Dropdown.Menu>
|
||||
<Dropdown.Item
|
||||
icon="pencil"
|
||||
text={faceGroup?.label ? 'Change Label' : 'Add Label'}
|
||||
text={
|
||||
faceGroup?.label
|
||||
? t(
|
||||
'people_page.face_group.action.change_label',
|
||||
'Change Label'
|
||||
)
|
||||
: t('people_page.face_group.action.add_label', 'Add Label')
|
||||
}
|
||||
onClick={() => setEditLabel(true)}
|
||||
/>
|
||||
<Dropdown.Item
|
||||
icon="object group"
|
||||
text="Merge Face"
|
||||
text={t('people_page.face_group.action.merge_face', 'Merge Face')}
|
||||
onClick={() => setMergeModalOpen(true)}
|
||||
/>
|
||||
<Dropdown.Item
|
||||
icon="object ungroup"
|
||||
text="Detach Faces"
|
||||
text={t(
|
||||
'people_page.face_group.action.detach_face',
|
||||
'Detach Face'
|
||||
)}
|
||||
onClick={() => setDetachModalOpen(true)}
|
||||
/>
|
||||
<Dropdown.Item
|
||||
icon="clone"
|
||||
text="Move Faces"
|
||||
text={t('people_page.face_group.action.move_faces', 'Move Faces')}
|
||||
onClick={() => setMoveModalOpen(true)}
|
||||
/>
|
||||
</Dropdown.Menu>
|
||||
|
@ -130,7 +144,7 @@ const FaceGroupTitle = ({ faceGroup }: FaceGroupTitleProps) => {
|
|||
<Input
|
||||
loading={setLabelLoading}
|
||||
ref={inputRef}
|
||||
placeholder="Label"
|
||||
placeholder={t('people_page.face_group.label_placeholder', 'Label')}
|
||||
icon="arrow right"
|
||||
value={inputValue}
|
||||
onKeyUp={onKeyUp}
|
||||
|
@ -143,9 +157,10 @@ const FaceGroupTitle = ({ faceGroup }: FaceGroupTitleProps) => {
|
|||
)
|
||||
}
|
||||
|
||||
return (
|
||||
let modals = null
|
||||
if (faceGroup) {
|
||||
modals = (
|
||||
<>
|
||||
{title}
|
||||
<MergeFaceGroupsModal
|
||||
open={mergeModalOpen}
|
||||
setOpen={setMergeModalOpen}
|
||||
|
@ -165,4 +180,12 @@ const FaceGroupTitle = ({ faceGroup }: FaceGroupTitleProps) => {
|
|||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{title}
|
||||
{modals}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default FaceGroupTitle
|
||||
|
|
|
@ -1,94 +0,0 @@
|
|||
import { gql, useMutation, useQuery } from '@apollo/client'
|
||||
import PropTypes from 'prop-types'
|
||||
import React, { useState } from 'react'
|
||||
import { useHistory } from 'react-router-dom'
|
||||
import { Button, Modal } from 'semantic-ui-react'
|
||||
import { MY_FACES_QUERY } from '../PeoplePage'
|
||||
import SelectFaceGroupTable from './SelectFaceGroupTable'
|
||||
|
||||
const COMBINE_FACES_MUTATION = gql`
|
||||
mutation combineFaces($destID: ID!, $srcID: ID!) {
|
||||
combineFaceGroups(
|
||||
destinationFaceGroupID: $destID
|
||||
sourceFaceGroupID: $srcID
|
||||
) {
|
||||
id
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
const MergeFaceGroupsModal = ({ open, setOpen, sourceFaceGroup }) => {
|
||||
const [selectedFaceGroup, setSelectedFaceGroup] = useState(null)
|
||||
|
||||
let history = useHistory()
|
||||
const { data } = useQuery(MY_FACES_QUERY)
|
||||
const [combineFacesMutation] = useMutation(COMBINE_FACES_MUTATION, {
|
||||
variables: {
|
||||
srcID: sourceFaceGroup?.id,
|
||||
},
|
||||
refetchQueries: [
|
||||
{
|
||||
query: MY_FACES_QUERY,
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
if (open == false) return null
|
||||
|
||||
const filteredFaceGroups =
|
||||
data?.myFaceGroups.filter(x => x.id != sourceFaceGroup?.id) ?? []
|
||||
|
||||
const mergeFaceGroups = () => {
|
||||
combineFacesMutation({
|
||||
variables: {
|
||||
destID: selectedFaceGroup.id,
|
||||
},
|
||||
}).then(() => {
|
||||
setOpen(false)
|
||||
history.push(`/people/${selectedFaceGroup.id}`)
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal
|
||||
onClose={() => setOpen(false)}
|
||||
onOpen={() => setOpen(true)}
|
||||
open={open}
|
||||
>
|
||||
<Modal.Header>Merge Face Groups</Modal.Header>
|
||||
<Modal.Content scrolling>
|
||||
<Modal.Description>
|
||||
<p>
|
||||
All images within this face group will be merged into the selected
|
||||
face group.
|
||||
</p>
|
||||
<SelectFaceGroupTable
|
||||
title="Select the destination face"
|
||||
faceGroups={filteredFaceGroups}
|
||||
selectedFaceGroup={selectedFaceGroup}
|
||||
setSelectedFaceGroup={setSelectedFaceGroup}
|
||||
/>
|
||||
</Modal.Description>
|
||||
</Modal.Content>
|
||||
<Modal.Actions>
|
||||
<Button onClick={() => setOpen(false)}>Cancel</Button>
|
||||
<Button
|
||||
disabled={selectedFaceGroup == null}
|
||||
content="Merge"
|
||||
labelPosition="right"
|
||||
icon="checkmark"
|
||||
onClick={() => mergeFaceGroups()}
|
||||
positive
|
||||
/>
|
||||
</Modal.Actions>
|
||||
</Modal>
|
||||
)
|
||||
}
|
||||
|
||||
MergeFaceGroupsModal.propTypes = {
|
||||
open: PropTypes.bool.isRequired,
|
||||
setOpen: PropTypes.func.isRequired,
|
||||
sourceFaceGroup: PropTypes.object,
|
||||
}
|
||||
|
||||
export default MergeFaceGroupsModal
|
|
@ -0,0 +1,124 @@
|
|||
import { gql, useMutation, useQuery } from '@apollo/client'
|
||||
import React, { useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useHistory } from 'react-router-dom'
|
||||
import { Button, Modal } from 'semantic-ui-react'
|
||||
import { isNil } from '../../../helpers/utils'
|
||||
import { MY_FACES_QUERY } from '../PeoplePage'
|
||||
import {
|
||||
myFaces,
|
||||
myFacesVariables,
|
||||
myFaces_myFaceGroups,
|
||||
} from '../__generated__/myFaces'
|
||||
import SelectFaceGroupTable from './SelectFaceGroupTable'
|
||||
import {
|
||||
combineFaces,
|
||||
combineFacesVariables,
|
||||
} from './__generated__/combineFaces'
|
||||
import { singleFaceGroup_faceGroup } from './__generated__/singleFaceGroup'
|
||||
|
||||
const COMBINE_FACES_MUTATION = gql`
|
||||
mutation combineFaces($destID: ID!, $srcID: ID!) {
|
||||
combineFaceGroups(
|
||||
destinationFaceGroupID: $destID
|
||||
sourceFaceGroupID: $srcID
|
||||
) {
|
||||
id
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
type MergeFaceGroupsModalProps = {
|
||||
open: boolean
|
||||
setOpen(open: boolean): void
|
||||
sourceFaceGroup: myFaces_myFaceGroups | singleFaceGroup_faceGroup
|
||||
}
|
||||
|
||||
const MergeFaceGroupsModal = ({
|
||||
open,
|
||||
setOpen,
|
||||
sourceFaceGroup,
|
||||
}: MergeFaceGroupsModalProps) => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
const [selectedFaceGroup, setSelectedFaceGroup] =
|
||||
useState<myFaces_myFaceGroups | singleFaceGroup_faceGroup | null>(null)
|
||||
|
||||
const history = useHistory()
|
||||
const { data } = useQuery<myFaces, myFacesVariables>(MY_FACES_QUERY)
|
||||
const [combineFacesMutation] = useMutation<
|
||||
combineFaces,
|
||||
combineFacesVariables
|
||||
>(COMBINE_FACES_MUTATION, {
|
||||
refetchQueries: [
|
||||
{
|
||||
query: MY_FACES_QUERY,
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
if (open == false) return null
|
||||
|
||||
const filteredFaceGroups =
|
||||
data?.myFaceGroups.filter(x => x.id != sourceFaceGroup?.id) ?? []
|
||||
|
||||
const mergeFaceGroups = () => {
|
||||
if (isNil(selectedFaceGroup)) throw new Error('No selected face group')
|
||||
|
||||
combineFacesMutation({
|
||||
variables: {
|
||||
srcID: sourceFaceGroup.id,
|
||||
destID: selectedFaceGroup.id,
|
||||
},
|
||||
}).then(() => {
|
||||
setOpen(false)
|
||||
history.push(`/people/${selectedFaceGroup.id}`)
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal
|
||||
onClose={() => setOpen(false)}
|
||||
onOpen={() => setOpen(true)}
|
||||
open={open}
|
||||
>
|
||||
<Modal.Header>
|
||||
{t('people_page.modal.merge_face_groups.title', 'Merge Face Groups')}
|
||||
</Modal.Header>
|
||||
<Modal.Content scrolling>
|
||||
<Modal.Description>
|
||||
<p>
|
||||
{t(
|
||||
'people_page.modal.merge_face_groups.description',
|
||||
'All images within this face group will be merged into the selected face group.'
|
||||
)}
|
||||
</p>
|
||||
<SelectFaceGroupTable
|
||||
title={t(
|
||||
'people_page.modal.merge_face_groups.destination_table.title',
|
||||
'Select the destination face'
|
||||
)}
|
||||
faceGroups={filteredFaceGroups}
|
||||
selectedFaceGroup={selectedFaceGroup}
|
||||
setSelectedFaceGroup={setSelectedFaceGroup}
|
||||
/>
|
||||
</Modal.Description>
|
||||
</Modal.Content>
|
||||
<Modal.Actions>
|
||||
<Button onClick={() => setOpen(false)}>
|
||||
{t('general.action.cancel', 'Cancel')}
|
||||
</Button>
|
||||
<Button
|
||||
disabled={selectedFaceGroup == null}
|
||||
content={t('people_page.modal.action.merge', 'Merge')}
|
||||
labelPosition="right"
|
||||
icon="checkmark"
|
||||
onClick={() => mergeFaceGroups()}
|
||||
positive
|
||||
/>
|
||||
</Modal.Actions>
|
||||
</Modal>
|
||||
)
|
||||
}
|
||||
|
||||
export default MergeFaceGroupsModal
|
|
@ -20,6 +20,7 @@ import {
|
|||
moveImageFaces,
|
||||
moveImageFacesVariables,
|
||||
} from './__generated__/moveImageFaces'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
const MOVE_IMAGE_FACES_MUTATION = gql`
|
||||
mutation moveImageFaces($faceIDs: [ID!]!, $destFaceGroupID: ID!) {
|
||||
|
@ -38,7 +39,7 @@ const MOVE_IMAGE_FACES_MUTATION = gql`
|
|||
type MoveImageFacesModalProps = {
|
||||
open: boolean
|
||||
setOpen: React.Dispatch<React.SetStateAction<boolean>>
|
||||
faceGroup?: singleFaceGroup_faceGroup
|
||||
faceGroup: singleFaceGroup_faceGroup
|
||||
}
|
||||
|
||||
const MoveImageFacesModal = ({
|
||||
|
@ -46,6 +47,8 @@ const MoveImageFacesModal = ({
|
|||
setOpen,
|
||||
faceGroup,
|
||||
}: MoveImageFacesModalProps) => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
const [selectedImageFaces, setSelectedImageFaces] = useState<
|
||||
(singleFaceGroup_faceGroup_imageFaces | myFaces_myFaceGroups_imageFaces)[]
|
||||
>([])
|
||||
|
@ -102,7 +105,7 @@ const MoveImageFacesModal = ({
|
|||
})
|
||||
}
|
||||
|
||||
const imageFaces = faceGroup?.imageFaces ?? []
|
||||
const imageFaces = faceGroup.imageFaces
|
||||
|
||||
let table = null
|
||||
if (!imagesSelected) {
|
||||
|
@ -111,7 +114,10 @@ const MoveImageFacesModal = ({
|
|||
imageFaces={imageFaces}
|
||||
selectedImageFaces={selectedImageFaces}
|
||||
setSelectedImageFaces={setSelectedImageFaces}
|
||||
title="Select images to move"
|
||||
title={t(
|
||||
'people_page.modal.move_image_faces.image_select_table.title',
|
||||
'Select images to move'
|
||||
)}
|
||||
/>
|
||||
)
|
||||
} else {
|
||||
|
@ -121,14 +127,17 @@ const MoveImageFacesModal = ({
|
|||
)
|
||||
table = (
|
||||
<SelectFaceGroupTable
|
||||
title="Select destination face group"
|
||||
title={t(
|
||||
'people_page.modal.move_image_faces.destination_face_group_table.title',
|
||||
'Select destination face group'
|
||||
)}
|
||||
faceGroups={filteredFaceGroups}
|
||||
selectedFaceGroup={selectedFaceGroup}
|
||||
setSelectedFaceGroup={setSelectedFaceGroup}
|
||||
/>
|
||||
)
|
||||
} else {
|
||||
table = <div>Loading...</div>
|
||||
table = <div>{t('general.loading.default', 'Loading...')}</div>
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -137,7 +146,10 @@ const MoveImageFacesModal = ({
|
|||
positiveButton = (
|
||||
<Button
|
||||
disabled={selectedImageFaces.length == 0}
|
||||
content="Next"
|
||||
content={t(
|
||||
'people_page.modal.move_image_faces.image_select_table.next_action',
|
||||
'Next'
|
||||
)}
|
||||
labelPosition="right"
|
||||
icon="arrow right"
|
||||
onClick={() => setImagesSelected(true)}
|
||||
|
@ -148,7 +160,10 @@ const MoveImageFacesModal = ({
|
|||
positiveButton = (
|
||||
<Button
|
||||
disabled={!selectedFaceGroup}
|
||||
content="Move image faces"
|
||||
content={t(
|
||||
'people_page.modal.move_image_faces.destination_face_group_table.move_action',
|
||||
'Move image faces'
|
||||
)}
|
||||
labelPosition="right"
|
||||
icon="checkmark"
|
||||
onClick={() => moveImageFaces()}
|
||||
|
@ -163,15 +178,24 @@ const MoveImageFacesModal = ({
|
|||
onOpen={() => setOpen(true)}
|
||||
open={open}
|
||||
>
|
||||
<Modal.Header>Move Image Faces</Modal.Header>
|
||||
<Modal.Header>
|
||||
{t('people_page.modal.move_image_faces.title', 'Move Image Faces')}
|
||||
</Modal.Header>
|
||||
<Modal.Content scrolling>
|
||||
<Modal.Description>
|
||||
<p>Move selected images of this face group to another face group</p>
|
||||
<p>
|
||||
{t(
|
||||
'people_page.modal.move_image_faces.description',
|
||||
'Move selected images of this face group to another face group'
|
||||
)}
|
||||
</p>
|
||||
{table}
|
||||
</Modal.Description>
|
||||
</Modal.Content>
|
||||
<Modal.Actions>
|
||||
<Button onClick={() => setOpen(false)}>Cancel</Button>
|
||||
<Button onClick={() => setOpen(false)}>
|
||||
{t('general.action.cancel', 'Cancel')}
|
||||
</Button>
|
||||
{positiveButton}
|
||||
</Modal.Actions>
|
||||
</Modal>
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import React, { useState, useEffect } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Input, Pagination, Table } from 'semantic-ui-react'
|
||||
import styled from 'styled-components'
|
||||
import FaceCircleImage from '../FaceCircleImage'
|
||||
|
@ -66,6 +67,8 @@ const SelectFaceGroupTable = ({
|
|||
setSelectedFaceGroup,
|
||||
title,
|
||||
}: SelectFaceGroupTableProps) => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
const PAGE_SIZE = 6
|
||||
|
||||
const [page, setPage] = useState(0)
|
||||
|
@ -106,7 +109,10 @@ const SelectFaceGroupTable = ({
|
|||
value={searchValue}
|
||||
onChange={e => setSearchValue(e.target.value)}
|
||||
icon="search"
|
||||
placeholder="Search faces..."
|
||||
placeholder={t(
|
||||
'people_page.table.select_face_group.search_faces_placeholder',
|
||||
'Search faces...'
|
||||
)}
|
||||
fluid
|
||||
/>
|
||||
</Table.HeaderCell>
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import React, { useEffect, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Checkbox, Input, Pagination, Table } from 'semantic-ui-react'
|
||||
import styled from 'styled-components'
|
||||
import { ProtectedImage } from '../../../components/photoGallery/ProtectedMedia'
|
||||
|
@ -67,6 +68,8 @@ const SelectImageFacesTable = ({
|
|||
setSelectedImageFaces,
|
||||
title,
|
||||
}: SelectImageFacesTable) => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
const PAGE_SIZE = 6
|
||||
|
||||
const [page, setPage] = useState(0)
|
||||
|
@ -115,7 +118,10 @@ const SelectImageFacesTable = ({
|
|||
value={searchValue}
|
||||
onChange={e => setSearchValue(e.target.value)}
|
||||
icon="search"
|
||||
placeholder="Search images..."
|
||||
placeholder={t(
|
||||
'people_page.table.select_image_faces.search_images_placeholder',
|
||||
'Search images...'
|
||||
)}
|
||||
fluid
|
||||
/>
|
||||
</Table.HeaderCell>
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { gql, useQuery } from '@apollo/client'
|
||||
import React, { useEffect, useReducer } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import PaginateLoader from '../../../components/PaginateLoader'
|
||||
import PhotoGallery from '../../../components/photoGallery/PhotoGallery'
|
||||
import { photoGalleryReducer } from '../../../components/photoGallery/photoGalleryReducer'
|
||||
|
@ -47,6 +48,8 @@ type SingleFaceGroupProps = {
|
|||
}
|
||||
|
||||
const SingleFaceGroup = ({ faceGroupID }: SingleFaceGroupProps) => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
const { data, error, loading, fetchMore } = useQuery<
|
||||
singleFaceGroup,
|
||||
singleFaceGroupVariables
|
||||
|
@ -94,7 +97,7 @@ const SingleFaceGroup = ({ faceGroupID }: SingleFaceGroupProps) => {
|
|||
/>
|
||||
<PaginateLoader
|
||||
active={!finishedLoadingMore && !loading}
|
||||
text="Loading more photos"
|
||||
text={t('general.loading.paginate.media', 'Loading more media')}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
|
Loading…
Reference in New Issue