mirror of
https://github.com/immich-app/immich.git
synced 2025-03-01 15:11:21 +01:00
pr feedback
This commit is contained in:
parent
7ced61e67d
commit
0f6e665d99
13 changed files with 74 additions and 32 deletions
|
@ -214,7 +214,7 @@ describe('/asset', () => {
|
||||||
id: user1Assets[0].id,
|
id: user1Assets[0].id,
|
||||||
isFavorite: false,
|
isFavorite: false,
|
||||||
people: {
|
people: {
|
||||||
faces: [
|
visiblePeople: [
|
||||||
{
|
{
|
||||||
birthDate: null,
|
birthDate: null,
|
||||||
id: expect.any(String),
|
id: expect.any(String),
|
||||||
|
@ -484,7 +484,7 @@ describe('/asset', () => {
|
||||||
id: user1Assets[0].id,
|
id: user1Assets[0].id,
|
||||||
isFavorite: true,
|
isFavorite: true,
|
||||||
people: {
|
people: {
|
||||||
faces: [
|
visiblePeople: [
|
||||||
{
|
{
|
||||||
birthDate: null,
|
birthDate: null,
|
||||||
id: expect.any(String),
|
id: expect.any(String),
|
||||||
|
|
|
@ -72,7 +72,7 @@ class AssetService {
|
||||||
final AssetResponseDto? dto =
|
final AssetResponseDto? dto =
|
||||||
await _apiService.assetApi.getAssetInfo(remoteId);
|
await _apiService.assetApi.getAssetInfo(remoteId);
|
||||||
|
|
||||||
return dto?.people?.faces;
|
return dto?.people?.visiblePeople;
|
||||||
} catch (error, stack) {
|
} catch (error, stack) {
|
||||||
log.severe(
|
log.severe(
|
||||||
'Error while getting remote asset info: ${error.toString()}',
|
'Error while getting remote asset info: ${error.toString()}',
|
||||||
|
|
2
mobile/openapi/doc/PeopleWithFacesResponseDto.md
generated
2
mobile/openapi/doc/PeopleWithFacesResponseDto.md
generated
|
@ -8,8 +8,8 @@ import 'package:openapi/api.dart';
|
||||||
## Properties
|
## Properties
|
||||||
Name | Type | Description | Notes
|
Name | Type | Description | Notes
|
||||||
------------ | ------------- | ------------- | -------------
|
------------ | ------------- | ------------- | -------------
|
||||||
**faces** | [**List<PersonWithFacesResponseDto>**](PersonWithFacesResponseDto.md) | | [default to const []]
|
|
||||||
**numberOfFaces** | **int** | |
|
**numberOfFaces** | **int** | |
|
||||||
|
**visiblePeople** | [**List<PersonWithFacesResponseDto>**](PersonWithFacesResponseDto.md) | | [default to const []]
|
||||||
|
|
||||||
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
|
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
|
||||||
|
|
||||||
|
|
|
@ -13,32 +13,32 @@ part of openapi.api;
|
||||||
class PeopleWithFacesResponseDto {
|
class PeopleWithFacesResponseDto {
|
||||||
/// Returns a new [PeopleWithFacesResponseDto] instance.
|
/// Returns a new [PeopleWithFacesResponseDto] instance.
|
||||||
PeopleWithFacesResponseDto({
|
PeopleWithFacesResponseDto({
|
||||||
this.faces = const [],
|
|
||||||
required this.numberOfFaces,
|
required this.numberOfFaces,
|
||||||
|
this.visiblePeople = const [],
|
||||||
});
|
});
|
||||||
|
|
||||||
List<PersonWithFacesResponseDto> faces;
|
|
||||||
|
|
||||||
int numberOfFaces;
|
int numberOfFaces;
|
||||||
|
|
||||||
|
List<PersonWithFacesResponseDto> visiblePeople;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) => identical(this, other) || other is PeopleWithFacesResponseDto &&
|
bool operator ==(Object other) => identical(this, other) || other is PeopleWithFacesResponseDto &&
|
||||||
_deepEquality.equals(other.faces, faces) &&
|
other.numberOfFaces == numberOfFaces &&
|
||||||
other.numberOfFaces == numberOfFaces;
|
_deepEquality.equals(other.visiblePeople, visiblePeople);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int get hashCode =>
|
int get hashCode =>
|
||||||
// ignore: unnecessary_parenthesis
|
// ignore: unnecessary_parenthesis
|
||||||
(faces.hashCode) +
|
(numberOfFaces.hashCode) +
|
||||||
(numberOfFaces.hashCode);
|
(visiblePeople.hashCode);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() => 'PeopleWithFacesResponseDto[faces=$faces, numberOfFaces=$numberOfFaces]';
|
String toString() => 'PeopleWithFacesResponseDto[numberOfFaces=$numberOfFaces, visiblePeople=$visiblePeople]';
|
||||||
|
|
||||||
Map<String, dynamic> toJson() {
|
Map<String, dynamic> toJson() {
|
||||||
final json = <String, dynamic>{};
|
final json = <String, dynamic>{};
|
||||||
json[r'faces'] = this.faces;
|
|
||||||
json[r'numberOfFaces'] = this.numberOfFaces;
|
json[r'numberOfFaces'] = this.numberOfFaces;
|
||||||
|
json[r'visiblePeople'] = this.visiblePeople;
|
||||||
return json;
|
return json;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,8 +50,8 @@ class PeopleWithFacesResponseDto {
|
||||||
final json = value.cast<String, dynamic>();
|
final json = value.cast<String, dynamic>();
|
||||||
|
|
||||||
return PeopleWithFacesResponseDto(
|
return PeopleWithFacesResponseDto(
|
||||||
faces: PersonWithFacesResponseDto.listFromJson(json[r'faces']),
|
|
||||||
numberOfFaces: mapValueOfType<int>(json, r'numberOfFaces')!,
|
numberOfFaces: mapValueOfType<int>(json, r'numberOfFaces')!,
|
||||||
|
visiblePeople: PersonWithFacesResponseDto.listFromJson(json[r'visiblePeople']),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
|
@ -99,8 +99,8 @@ class PeopleWithFacesResponseDto {
|
||||||
|
|
||||||
/// The list of required keys that must be present in a JSON.
|
/// The list of required keys that must be present in a JSON.
|
||||||
static const requiredKeys = <String>{
|
static const requiredKeys = <String>{
|
||||||
'faces',
|
|
||||||
'numberOfFaces',
|
'numberOfFaces',
|
||||||
|
'visiblePeople',
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,13 +16,13 @@ void main() {
|
||||||
// final instance = PeopleWithFacesResponseDto();
|
// final instance = PeopleWithFacesResponseDto();
|
||||||
|
|
||||||
group('test PeopleWithFacesResponseDto', () {
|
group('test PeopleWithFacesResponseDto', () {
|
||||||
// List<PersonWithFacesResponseDto> faces (default value: const [])
|
// int numberOfFaces
|
||||||
test('to test the property `faces`', () async {
|
test('to test the property `numberOfFaces`', () async {
|
||||||
// TODO
|
// TODO
|
||||||
});
|
});
|
||||||
|
|
||||||
// int numberOfFaces
|
// List<PersonWithFacesResponseDto> visiblePeople (default value: const [])
|
||||||
test('to test the property `numberOfFaces`', () async {
|
test('to test the property `visiblePeople`', () async {
|
||||||
// TODO
|
// TODO
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -9014,19 +9014,19 @@
|
||||||
},
|
},
|
||||||
"PeopleWithFacesResponseDto": {
|
"PeopleWithFacesResponseDto": {
|
||||||
"properties": {
|
"properties": {
|
||||||
"faces": {
|
"numberOfFaces": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"visiblePeople": {
|
||||||
"items": {
|
"items": {
|
||||||
"$ref": "#/components/schemas/PersonWithFacesResponseDto"
|
"$ref": "#/components/schemas/PersonWithFacesResponseDto"
|
||||||
},
|
},
|
||||||
"type": "array"
|
"type": "array"
|
||||||
},
|
|
||||||
"numberOfFaces": {
|
|
||||||
"type": "integer"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"required": [
|
"required": [
|
||||||
"faces",
|
"numberOfFaces",
|
||||||
"numberOfFaces"
|
"visiblePeople"
|
||||||
],
|
],
|
||||||
"type": "object"
|
"type": "object"
|
||||||
},
|
},
|
||||||
|
|
|
@ -101,8 +101,8 @@ export type PersonWithFacesResponseDto = {
|
||||||
thumbnailPath: string;
|
thumbnailPath: string;
|
||||||
};
|
};
|
||||||
export type PeopleWithFacesResponseDto = {
|
export type PeopleWithFacesResponseDto = {
|
||||||
faces: PersonWithFacesResponseDto[];
|
|
||||||
numberOfFaces: number;
|
numberOfFaces: number;
|
||||||
|
visiblePeople: PersonWithFacesResponseDto[];
|
||||||
};
|
};
|
||||||
export type SmartInfoResponseDto = {
|
export type SmartInfoResponseDto = {
|
||||||
objects?: string[] | null;
|
objects?: string[] | null;
|
||||||
|
|
|
@ -78,7 +78,7 @@ const peopleWithFaces = (faces: AssetFaceEntity[]): PeopleWithFacesResponseDto =
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return { faces: result, numberOfFaces: faces.length };
|
return { visiblePeople: result, numberOfFaces: faces.length };
|
||||||
};
|
};
|
||||||
|
|
||||||
export function mapAsset(entity: AssetEntity, options: AssetMapOptions = {}): AssetResponseDto {
|
export function mapAsset(entity: AssetEntity, options: AssetMapOptions = {}): AssetResponseDto {
|
||||||
|
|
|
@ -78,7 +78,7 @@ export class PersonWithFacesResponseDto extends PersonResponseDto {
|
||||||
}
|
}
|
||||||
|
|
||||||
export class PeopleWithFacesResponseDto {
|
export class PeopleWithFacesResponseDto {
|
||||||
faces!: PersonWithFacesResponseDto[];
|
visiblePeople!: PersonWithFacesResponseDto[];
|
||||||
@ApiProperty({ type: 'integer' })
|
@ApiProperty({ type: 'integer' })
|
||||||
numberOfFaces!: number;
|
numberOfFaces!: number;
|
||||||
}
|
}
|
||||||
|
|
|
@ -449,6 +449,18 @@ describe(PersonService.name, () => {
|
||||||
await expect(sut.unassignFace(authStub.admin, faceStub.face1.id)).resolves.toStrictEqual(
|
await expect(sut.unassignFace(authStub.admin, faceStub.face1.id)).resolves.toStrictEqual(
|
||||||
mapFaces(faceStub.unassignedFace, authStub.admin),
|
mapFaces(faceStub.unassignedFace, authStub.admin),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
expect(mediaMock.generateThumbnail).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not unassign a face if user has no create access', async () => {
|
||||||
|
personMock.getFaceById.mockResolvedValueOnce(faceStub.face1);
|
||||||
|
accessMock.person.checkOwnerAccess.mockResolvedValue(new Set([personStub.noName.id]));
|
||||||
|
personMock.reassignFace.mockResolvedValue(1);
|
||||||
|
personMock.getRandomFace.mockResolvedValue(null);
|
||||||
|
personMock.getFaceById.mockResolvedValueOnce(faceStub.unassignedFace);
|
||||||
|
|
||||||
|
await expect(sut.unassignFace(authStub.admin, faceStub.face1.id)).rejects.toBeInstanceOf(BadRequestException);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -465,6 +477,18 @@ describe(PersonService.name, () => {
|
||||||
sut.unassignFaces(authStub.admin, { data: [{ assetId: faceStub.face1.id, personId: 'person-1' }] }),
|
sut.unassignFaces(authStub.admin, { data: [{ assetId: faceStub.face1.id, personId: 'person-1' }] }),
|
||||||
).resolves.toStrictEqual([{ id: 'assetFaceId1', success: true }]);
|
).resolves.toStrictEqual([{ id: 'assetFaceId1', success: true }]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should not unassign a face if the user has no create access', async () => {
|
||||||
|
personMock.getFacesByIds.mockResolvedValueOnce([faceStub.face1]);
|
||||||
|
accessMock.person.checkOwnerAccess.mockResolvedValue(new Set([personStub.noName.id]));
|
||||||
|
personMock.reassignFace.mockResolvedValue(1);
|
||||||
|
personMock.getRandomFace.mockResolvedValue(null);
|
||||||
|
personMock.getFaceById.mockResolvedValueOnce(faceStub.unassignedFace);
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
sut.unassignFaces(authStub.admin, { data: [{ assetId: faceStub.face1.id, personId: 'person-1' }] }),
|
||||||
|
).rejects.toBeInstanceOf(BadRequestException);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('handlePersonCleanup', () => {
|
describe('handlePersonCleanup', () => {
|
||||||
|
|
|
@ -218,7 +218,7 @@
|
||||||
<div class="flex h-10 w-full items-center justify-between">
|
<div class="flex h-10 w-full items-center justify-between">
|
||||||
<h2>PEOPLE</h2>
|
<h2>PEOPLE</h2>
|
||||||
<div class="flex gap-2 items-center">
|
<div class="flex gap-2 items-center">
|
||||||
{#if people.faces.some((person) => person.isHidden)}
|
{#if people.visiblePeople.some((person) => person.isHidden)}
|
||||||
<CircleIconButton
|
<CircleIconButton
|
||||||
title="Show hidden people"
|
title="Show hidden people"
|
||||||
icon={showingHiddenPeople ? mdiEyeOff : mdiEye}
|
icon={showingHiddenPeople ? mdiEyeOff : mdiEye}
|
||||||
|
@ -239,7 +239,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mt-2 flex flex-wrap gap-2">
|
<div class="mt-2 flex flex-wrap gap-2">
|
||||||
{#each people.faces as person (person.id)}
|
{#each people.visiblePeople as person (person.id)}
|
||||||
{#if showingHiddenPeople || !person.isHidden}
|
{#if showingHiddenPeople || !person.isHidden}
|
||||||
<a
|
<a
|
||||||
class="w-[90px]"
|
class="w-[90px]"
|
||||||
|
|
|
@ -64,6 +64,10 @@
|
||||||
let loaderLoadingDoneTimeout: ReturnType<typeof setTimeout>;
|
let loaderLoadingDoneTimeout: ReturnType<typeof setTimeout>;
|
||||||
let automaticRefreshTimeout: ReturnType<typeof setTimeout>;
|
let automaticRefreshTimeout: ReturnType<typeof setTimeout>;
|
||||||
|
|
||||||
|
$: mapFacesToBeCreated = Object.entries(selectedPersonToAdd)
|
||||||
|
.filter(([_, value]) => value.person === null)
|
||||||
|
.map(([key, _]) => key);
|
||||||
|
|
||||||
const thumbnailWidth = '90px';
|
const thumbnailWidth = '90px';
|
||||||
|
|
||||||
const generatePeopleWithoutFaces = async () => {
|
const generatePeopleWithoutFaces = async () => {
|
||||||
|
@ -94,9 +98,23 @@
|
||||||
isShowLoadingPeople = false;
|
isShowLoadingPeople = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* we wait for the server to create the feature photo for:
|
||||||
|
* - people which has been reassigned to a new person
|
||||||
|
* - faces removed assigned to a new person
|
||||||
|
*
|
||||||
|
* if after 15 seconds the server has not generated the feature photos,
|
||||||
|
* we go back to the detail-panel
|
||||||
|
*/
|
||||||
const onPersonThumbnail = (personId: string) => {
|
const onPersonThumbnail = (personId: string) => {
|
||||||
assetFaceGenerated.push(personId);
|
assetFaceGenerated.push(personId);
|
||||||
if (isEqual(assetFaceGenerated, peopleToCreate) && loaderLoadingDoneTimeout && automaticRefreshTimeout) {
|
|
||||||
|
if (
|
||||||
|
isEqual(assetFaceGenerated, peopleToCreate) &&
|
||||||
|
isEqual(assetFaceGenerated, mapFacesToBeCreated) &&
|
||||||
|
loaderLoadingDoneTimeout &&
|
||||||
|
automaticRefreshTimeout
|
||||||
|
) {
|
||||||
clearTimeout(loaderLoadingDoneTimeout);
|
clearTimeout(loaderLoadingDoneTimeout);
|
||||||
clearTimeout(automaticRefreshTimeout);
|
clearTimeout(automaticRefreshTimeout);
|
||||||
onRefresh();
|
onRefresh();
|
||||||
|
|
|
@ -45,7 +45,7 @@ export function getAltText(asset: AssetResponseDto) {
|
||||||
altText += ` in ${asset.exifInfo.city}, ${asset.exifInfo.country}`;
|
altText += ` in ${asset.exifInfo.city}, ${asset.exifInfo.country}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
const names = asset.people?.faces.filter((p) => p.name).map((p) => p.name) ?? [];
|
const names = asset.people?.visiblePeople.filter((p) => p.name).map((p) => p.name) ?? [];
|
||||||
if (names.length == 1) {
|
if (names.length == 1) {
|
||||||
altText += ` with ${names[0]}`;
|
altText += ` with ${names[0]}`;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue