Start on UI for faces

This commit is contained in:
viktorstrate
2021-02-16 12:41:34 +01:00
parent 67b812ed21
commit c3eda0feab
6 changed files with 164 additions and 13 deletions

View File

@@ -87,31 +87,28 @@ func (fr *FaceRectangle) Scan(value interface{}) error {
return fmt.Errorf("Invalid face rectangle format, expected 4 values, got %d", len(slices))
}
minX, err := strconv.ParseFloat(slices[0], 32)
var err error
fr.MinX, err = strconv.ParseFloat(slices[0], 32)
if err != nil {
return err
}
maxX, err := strconv.ParseFloat(slices[0], 32)
fr.MaxX, err = strconv.ParseFloat(slices[1], 32)
if err != nil {
return err
}
minY, err := strconv.ParseFloat(slices[0], 32)
fr.MinY, err = strconv.ParseFloat(slices[2], 32)
if err != nil {
return err
}
maxY, err := strconv.ParseFloat(slices[0], 32)
fr.MaxY, err = strconv.ParseFloat(slices[3], 32)
if err != nil {
return err
}
fr.MinX = float64(minX)
fr.MinX = float64(maxX)
fr.MinX = float64(minY)
fr.MinX = float64(maxY)
return nil
}

View File

@@ -30,10 +30,10 @@ func (r *queryResolver) MyFaceGroups(ctx context.Context, paginate *models.Pagin
faceGroupMap := make(map[int][]models.ImageFace)
for _, face := range imageFaces {
group, found := faceGroupMap[face.FaceGroupID]
_, found := faceGroupMap[face.FaceGroupID]
if found {
group = append(group, *face)
faceGroupMap[face.FaceGroupID] = append(faceGroupMap[face.FaceGroupID], *face)
} else {
faceGroupMap[face.FaceGroupID] = make([]models.ImageFace, 1)
faceGroupMap[face.FaceGroupID][0] = *face

View File

@@ -117,6 +117,10 @@ export const SideMenu = () => {
<SideButtonLabel>Places</SideButtonLabel>
</SideButton>
) : null}
<SideButton to="/faces" exact>
<Icon name="user outline" />
<SideButtonLabel>Faces</SideButtonLabel>
</SideButton>
{isAdmin ? (
<SideButton to="/settings" exact>
<Icon name="settings" />

View File

@@ -0,0 +1,104 @@
import React from 'react'
import PropTypes from 'prop-types'
import { gql, useQuery } from '@apollo/client'
import Layout from '../../Layout'
import { ProtectedImage } from '../../components/photoGallery/ProtectedMedia'
import styled from 'styled-components'
import { Link } from 'react-router-dom'
import SingleFaceGroup from './SingleFaceGroup'
const MY_FACES_QUERY = gql`
query myFaces {
myFaceGroups {
id
label
imageFaces {
id
rectangle {
minX
maxX
minY
maxY
}
media {
id
thumbnail {
url
width
height
}
}
}
}
}
`
const CircleImageWrapper = styled.div`
border-radius: 50%;
width: 150px;
height: 150px;
object-fit: fill;
margin: 12px;
overflow: hidden;
`
const FaceImage = styled(ProtectedImage)`
width: 100%;
height: 100%;
object-fit: cover;
`
const FaceGroup = ({ group }) => (
<Link to={`/faces/${group.id}`}>
<CircleImageWrapper>
<FaceImage src={group.imageFaces[0].media.thumbnail.url} />
</CircleImageWrapper>
</Link>
)
FaceGroup.propTypes = {
group: PropTypes.any,
}
const FaceGroupsWrapper = styled.div`
display: flex;
flex-wrap: wrap;
`
const FacesPage = ({ match }) => {
const { data, error } = useQuery(MY_FACES_QUERY)
if (error) {
return error.message
}
const faceGroup = match.params.face
if (faceGroup) {
return (
<Layout>
<SingleFaceGroup
faceGroup={data?.myFaceGroups?.find(x => x.id == faceGroup)}
/>
</Layout>
)
}
let faces = null
if (data) {
faces = data.myFaceGroups.map(faceGroup => (
<FaceGroup key={faceGroup.id} group={faceGroup} />
))
}
return (
<Layout>
<FaceGroupsWrapper>{faces}</FaceGroupsWrapper>
</Layout>
)
}
FacesPage.propTypes = {
match: PropTypes.object.isRequired,
}
export default FacesPage

View File

@@ -0,0 +1,44 @@
import React from 'react'
import PropTypes from 'prop-types'
import { ProtectedImage } from '../../components/photoGallery/ProtectedMedia'
import PhotoGallery from '../../components/photoGallery/PhotoGallery'
const ImageFace = ({ imageFace }) => {
return (
<div>
Image face: {imageFace.id}
<ProtectedImage src={imageFace.media.thumbnail.url} />
</div>
)
}
ImageFace.propTypes = {
imageFace: PropTypes.object.isRequired,
}
const SingleFaceGroup = ({ faceGroup }) => {
if (!faceGroup) {
return null
}
// const images = faceGroup.imageFaces.map(imgFace => (
// <ImageFace key={imgFace.id} imageFace={imgFace} />
// ))
const media = faceGroup.imageFaces.map(x => x.media)
return (
<div>
Face group: {faceGroup.id}
<div>
<PhotoGallery media={media} loading={false} setPresenting={() => {}} />
</div>
</div>
)
}
SingleFaceGroup.propTypes = {
faceGroup: PropTypes.object,
}
export default SingleFaceGroup

View File

@@ -14,6 +14,7 @@ const AlbumPage = React.lazy(() => import('../../Pages/AlbumPage/AlbumPage'))
const PhotosPage = React.lazy(() => import('../../Pages/PhotosPage/PhotosPage'))
const PlacesPage = React.lazy(() => import('../../Pages/PlacesPage/PlacesPage'))
const SharePage = React.lazy(() => import('../../Pages/SharePage/SharePage'))
const FacesPage = React.lazy(() => import('../../Pages/FacesPage/FacesPage'))
const LoginPage = React.lazy(() => import('../../Pages/LoginPage/LoginPage'))
const InitialSetupPage = React.lazy(() =>
@@ -44,9 +45,10 @@ const Routes = () => {
<Route path="/initialSetup" component={InitialSetupPage} />
<Route path="/share" component={SharePage} />
<AuthorizedRoute exact path="/albums" component={AlbumsPage} />
<AuthorizedRoute path="/album/:id/:subPage?" component={AlbumPage} />
<AuthorizedRoute path="/photos/:subPage?" component={PhotosPage} />
<AuthorizedRoute path="/album/:id" component={AlbumPage} />
<AuthorizedRoute path="/photos" component={PhotosPage} />
<AuthorizedRoute path="/places" component={PlacesPage} />
<AuthorizedRoute path="/faces/:face?" component={FacesPage} />
<AuthorizedRoute admin path="/settings" component={SettingsPage} />
<Route path="/" exact render={() => <Redirect to="/photos" />} />
<Route render={() => <div>Page not found</div>} />