diff --git a/client/src/App.css b/client/src/App.css
index 9584df9..af6ba70 100644
--- a/client/src/App.css
+++ b/client/src/App.css
@@ -49,10 +49,9 @@ div {
user-select: none;
overflow-y: scroll;
overflow-x: clip;
- height: 100%;
width: 100%;
gap: 0.25rem;
- grid-template-columns: repeat(auto-fill, minmax(8.5rem, auto));
+ grid-template-columns: repeat(auto-fill, minmax(4.25rem, auto));
}
.Face {
@@ -73,14 +72,29 @@ div {
flex-direction: column;
}
+.Viewer {
+ display: flex;
+ flex-direction: column;
+ position: relative;
+}
+
.PhotoPanel {
display: flex;
flex-direction: column;
- height: 100%;
- width: 100%;
justify-content: center;
}
+.Guess {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+}
+
+button {
+ padding: 0.5rem;
+ min-width: 4rem;
+}
+
.Image .FaceBox {
border: 1px solid red;
position: absolute;
@@ -94,9 +108,6 @@ div {
.Image {
display: flex;
position: relative;
- background-size: contain !important;
- background-repeat: no-repeat no-repeat !important;
- background-position: 50% 50% !important;
}
.PhotoPanel .FaceInfo {
@@ -112,6 +123,10 @@ div {
margin-top: 0.25rem;
}
+.Identities .UnknownFace {
+ font-size: 2rem;
+}
+
.UnknownFace {
display: flex;
align-items: center;
@@ -158,6 +173,11 @@ div {
height: 10rem;
}
+.Identities .Face .Image {
+ min-width: 4rem;
+ min-height: 4rem;
+}
+
.Face .Image {
position: relative;
box-sizing: border-box;
@@ -186,6 +206,13 @@ div {
align-items: flex-start;
}
+
+.Viewer .PhotoPanel img {
+ object-fit: contain;
+ max-width: 100%;
+ max-height: 100%;
+}
+
.Image img {
object-fit: cover; /* contain */
width: 100%;
@@ -195,7 +222,7 @@ div {
.Cluster .Faces {
display: grid;
gap: 0.25rem;
- grid-template-columns: repeat(auto-fill, minmax(8rem, 1fr));
+ grid-template-columns: repeat(auto-fill, minmax(8.5rem, 1fr));
width: 100%;
flex-wrap: wrap;
}
\ No newline at end of file
diff --git a/client/src/App.tsx b/client/src/App.tsx
index 04c78d9..d950ee0 100644
--- a/client/src/App.tsx
+++ b/client/src/App.tsx
@@ -129,12 +129,7 @@ const Photo = ({ photoId, onFaceClick }: any) => {
{
@@ -326,20 +321,6 @@ const Cluster = ({
});
await res.json();
setIdentity({ ...identity, ...values });
- setIdentities(
- [...identities]
- .sort((A: IdentityData, B: IdentityData) => {
- /* Sort the Unknown (-1) identity to the end */
- if (A.identityId === -1) {
- return +1;
- }
- if (B.identityId === -1) {
- return -1;
- }
- /* Otherwise sort alphabetically by displayName */
- return A.displayName.localeCompare(B.displayName);
- })
- );
} catch (error) {
console.error(error);
}
@@ -361,20 +342,6 @@ const Cluster = ({
});
const created = await res.json();
setIdentity(created);
- setIdentities(
- [created, ...identities]
- .sort((A: IdentityData, B: IdentityData) => {
- /* Sort the Unknown (-1) identity to the end */
- if (A.identityId === -1) {
- return +1;
- }
- if (B.identityId === -1) {
- return -1;
- }
- /* Otherwise sort alphabetically by displayName */
- return A.displayName.localeCompare(B.displayName);
- })
- );
} catch (error) {
console.error(error);
}
@@ -537,6 +504,62 @@ const Button = ({ onClick, children }: any) => {
);
};
+/* returns true if update to identities array occurred */
+const updateIdentityReferences = (
+ identities: IdentityData[],
+ identity: IdentityData) : boolean => {
+
+ if (identity.identityId === -1) {
+ console.warn('Identity Unknown (-1) attempting to be updated');
+ return false;
+ }
+
+ const targetIndex = identities.findIndex(
+ x => x.identityId === identity.identityId);
+ if (targetIndex === -1) {
+ identities.push(identity);
+ return true;
+ }
+ const target = identities[targetIndex];
+
+ /*
+ IdentityData fields we check to make sure they are the same:
+
+ lastName: string,
+ middleName: string,
+ firstName: string,
+ displayName: string,
+ facesCount: number,
+ faceId: number
+ !identityId: number
+ !relatedFaces: FaceData[],
+ !descriptors: number[],
+ */
+
+ let same = true;
+
+ [ 'lastName', 'firstName', 'middleName',
+ 'displayName', 'faceId', 'facesCount', 'faceId' ]
+ .forEach((field: string) => {
+ same = same && (target as any)[field] === (identity as any)[field];
+ });
+
+ if (same) {
+ return false;
+ }
+
+ identities[targetIndex] = {
+ ...identity,
+ relatedFaces: target.relatedFaces
+ };
+
+ /* relatedFaces is a list of references to identity */
+ identity.relatedFaces.forEach(face => {
+ face.identity = identity;
+ });
+
+ return true;
+};
const App = () => {
const { identityId, faceId } = useParams();
@@ -574,32 +597,28 @@ const App = () => {
/* If the identity changes, update its entry in the identities list
* NOTE: Blocks update to 'Unknown' (-1) fake identity */
useEffect(() => {
- if (!identity || identities.length === 0 || identity.identityId === -1) {
+ if (identity.identityId === -1) {
return;
}
- for (let key in identities) {
- if (identities[key].identityId === identity.identityId) {
- let same = true;
- [ 'displayName', 'firstName', 'lastName', 'middleName' ]
- .forEach((field: string) => {
- same = same
- && (identities[key] as any)[field] === (identity as any)[field];
- });
- if (!same) {
- console.log(`Updating `, identity, identities[key]);
- identities[key] = {
- ...identity,
- relatedFaces: identities[key].relatedFaces
- };
- /* relatedFaces is a list of references to identity */
- identity.relatedFaces.forEach(face => {
- face.identity = identity;
- });
- setIdentities([...identities]);
- }
- return;
- }
+
+ if (!updateIdentityReferences(identities, identity)) {
+ return;
}
+
+ setIdentities(
+ [...identities]
+ .sort((A: IdentityData, B: IdentityData) => {
+ /* Sort the Unknown (-1) identity to the end */
+ if (A.identityId === -1) {
+ return +1;
+ }
+ if (B.identityId === -1) {
+ return -1;
+ }
+ /* Otherwise sort alphabetically by displayName */
+ return A.displayName.localeCompare(B.displayName);
+ })
+ );
}, [identity, setIdentities, identities]);
/* If the identity changes, scroll it into view in the Identities list */
@@ -720,7 +739,7 @@ const App = () => {
}
};
- const markSelectedIncorrectIdentity = async () => {
+ const removeFaceFromIdentity = async () => {
if (!identity) {
return;
}
@@ -735,9 +754,9 @@ const App = () => {
removeFacesFromIdentity(results.removed);
deselectAll();
- if (identity.faceId !== results.faceId) {
- setIdentity({...identity, ...{ faceId: results.faceId }});
- setIdentities([...identities]);
+ if (results.faceId !== undefined
+ && identity.faceId !== results.faceId) {
+ setIdentity({...identity, faceId: results.faceId });
}
} catch (error) {
console.error(error);
@@ -778,7 +797,6 @@ const App = () => {
target.faceId = results.faceId;
}
deselectAll();
- setIdentities([...identities]);
} catch (error) {
console.error(error);
}
@@ -798,7 +816,7 @@ const App = () => {
}
};
- const markSelectedNotFace = async () => {
+ const updateFasAsNotFace = async () => {
try {
const res = await window.fetch(
`${base}/api/v1/faces`, {
@@ -871,7 +889,8 @@ const App = () => {
e.stopPropagation();
e.preventDefault();
- if (identity.identityId !== identityId) {
+ if (identity.identityId !== identityId
+ || identity.facesCount === 0) {
[...document.querySelectorAll('.Cluster .Faces img')]
.forEach((img: any) => {
img.src = 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7';
@@ -901,8 +920,8 @@ const App = () => {
>}
{ selected.length !== 0 && <>
-
-
+
+
>}
@@ -913,18 +932,17 @@ const App = () => {
- {image === 0 && Select image to view
}
- {image !== 0 && }
- {guess !== undefined && guess.identity &&
-
-
}
+
+ {image === 0 &&
Select image to view
}
+ {image !== 0 &&
}
+ {guess !== undefined && guess.identity &&
+
+
}
+
@@ -932,7 +950,7 @@ const App = () => {
Loading...
}
{ loaded &&