mirror of
https://github.com/immich-app/immich.git
synced 2025-01-04 02:46:47 +01:00
fix(web): revert smart merge (#6504)
* revert smart merge * fix test * fix test * Remove Slider file
This commit is contained in:
parent
df27460f1c
commit
07b874edda
8 changed files with 74 additions and 147 deletions
|
@ -999,7 +999,7 @@ describe(PersonService.name, () => {
|
||||||
expect(accessMock.person.checkOwnerAccess).toHaveBeenCalledWith(authStub.admin.user.id, new Set(['person-1']));
|
expect(accessMock.person.checkOwnerAccess).toHaveBeenCalledWith(authStub.admin.user.id, new Set(['person-1']));
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should merge two people without smart merge', async () => {
|
it('should merge two people', async () => {
|
||||||
personMock.getById.mockResolvedValueOnce(personStub.primaryPerson);
|
personMock.getById.mockResolvedValueOnce(personStub.primaryPerson);
|
||||||
personMock.getById.mockResolvedValueOnce(personStub.mergePerson);
|
personMock.getById.mockResolvedValueOnce(personStub.mergePerson);
|
||||||
accessMock.person.checkOwnerAccess.mockResolvedValueOnce(new Set(['person-1']));
|
accessMock.person.checkOwnerAccess.mockResolvedValueOnce(new Set(['person-1']));
|
||||||
|
@ -1014,33 +1014,6 @@ describe(PersonService.name, () => {
|
||||||
oldPersonId: personStub.mergePerson.id,
|
oldPersonId: personStub.mergePerson.id,
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(personMock.update).not.toHaveBeenCalled();
|
|
||||||
|
|
||||||
expect(accessMock.person.checkOwnerAccess).toHaveBeenCalledWith(authStub.admin.user.id, new Set(['person-1']));
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should merge two people with smart merge', async () => {
|
|
||||||
personMock.getById.mockResolvedValueOnce(personStub.randomPerson);
|
|
||||||
personMock.getById.mockResolvedValueOnce(personStub.primaryPerson);
|
|
||||||
personMock.update.mockResolvedValue({ ...personStub.randomPerson, name: personStub.primaryPerson.name });
|
|
||||||
accessMock.person.checkOwnerAccess.mockResolvedValueOnce(new Set(['person-3']));
|
|
||||||
accessMock.person.checkOwnerAccess.mockResolvedValueOnce(new Set(['person-1']));
|
|
||||||
|
|
||||||
await expect(sut.mergePerson(authStub.admin, 'person-3', { ids: ['person-1'] })).resolves.toEqual([
|
|
||||||
{ id: 'person-1', success: true },
|
|
||||||
]);
|
|
||||||
|
|
||||||
expect(personMock.reassignFaces).toHaveBeenCalledWith({
|
|
||||||
newPersonId: personStub.randomPerson.id,
|
|
||||||
oldPersonId: personStub.primaryPerson.id,
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(personMock.update).toHaveBeenCalledWith({
|
|
||||||
id: personStub.randomPerson.id,
|
|
||||||
name: personStub.primaryPerson.name,
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(personMock.delete).toHaveBeenCalledWith([personStub.primaryPerson]);
|
|
||||||
expect(accessMock.person.checkOwnerAccess).toHaveBeenCalledWith(authStub.admin.user.id, new Set(['person-1']));
|
expect(accessMock.person.checkOwnerAccess).toHaveBeenCalledWith(authStub.admin.user.id, new Set(['person-1']));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1072,8 +1045,8 @@ describe(PersonService.name, () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should handle an error reassigning faces', async () => {
|
it('should handle an error reassigning faces', async () => {
|
||||||
personMock.getById.mockResolvedValueOnce(personStub.primaryPerson);
|
personMock.getById.mockResolvedValue(personStub.primaryPerson);
|
||||||
personMock.getById.mockResolvedValueOnce(personStub.mergePerson);
|
personMock.getById.mockResolvedValue(personStub.mergePerson);
|
||||||
personMock.reassignFaces.mockRejectedValue(new Error('update failed'));
|
personMock.reassignFaces.mockRejectedValue(new Error('update failed'));
|
||||||
accessMock.person.checkOwnerAccess.mockResolvedValueOnce(new Set(['person-1']));
|
accessMock.person.checkOwnerAccess.mockResolvedValueOnce(new Set(['person-1']));
|
||||||
accessMock.person.checkOwnerAccess.mockResolvedValueOnce(new Set(['person-2']));
|
accessMock.person.checkOwnerAccess.mockResolvedValueOnce(new Set(['person-2']));
|
||||||
|
|
|
@ -533,7 +533,7 @@ export class PersonService {
|
||||||
async mergePerson(auth: AuthDto, id: string, dto: MergePersonDto): Promise<BulkIdResponseDto[]> {
|
async mergePerson(auth: AuthDto, id: string, dto: MergePersonDto): Promise<BulkIdResponseDto[]> {
|
||||||
const mergeIds = dto.ids;
|
const mergeIds = dto.ids;
|
||||||
await this.access.requirePermission(auth, Permission.PERSON_WRITE, id);
|
await this.access.requirePermission(auth, Permission.PERSON_WRITE, id);
|
||||||
let primaryPerson = await this.findOrFail(id);
|
const primaryPerson = await this.findOrFail(id);
|
||||||
const primaryName = primaryPerson.name || primaryPerson.id;
|
const primaryName = primaryPerson.name || primaryPerson.id;
|
||||||
|
|
||||||
const results: BulkIdResponseDto[] = [];
|
const results: BulkIdResponseDto[] = [];
|
||||||
|
@ -554,19 +554,6 @@ export class PersonService {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const update: Partial<PersonEntity> = {};
|
|
||||||
if (!primaryPerson.name && mergePerson.name) {
|
|
||||||
update.name = mergePerson.name;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!primaryPerson.birthDate && mergePerson.birthDate) {
|
|
||||||
update.birthDate = mergePerson.birthDate;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Object.keys(update).length > 0) {
|
|
||||||
primaryPerson = await this.repository.update({ id: primaryPerson.id, ...update });
|
|
||||||
}
|
|
||||||
|
|
||||||
const mergeName = mergePerson.name || mergePerson.id;
|
const mergeName = mergePerson.name || mergePerson.id;
|
||||||
const mergeData: UpdateFacesData = { oldPersonId: mergeId, newPersonId: id };
|
const mergeData: UpdateFacesData = { oldPersonId: mergeId, newPersonId: id };
|
||||||
this.logger.log(`Merging ${mergeName} into ${primaryName}`);
|
this.logger.log(`Merging ${mergeName} into ${primaryName}`);
|
||||||
|
@ -581,6 +568,7 @@ export class PersonService {
|
||||||
results.push({ id: mergeId, success: false, error: BulkIdErrorReason.UNKNOWN });
|
results.push({ id: mergeId, success: false, error: BulkIdErrorReason.UNKNOWN });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
14
server/test/fixtures/person.stub.ts
vendored
14
server/test/fixtures/person.stub.ts
vendored
|
@ -128,18 +128,4 @@ export const personStub = {
|
||||||
faceAsset: null,
|
faceAsset: null,
|
||||||
isHidden: false,
|
isHidden: false,
|
||||||
}),
|
}),
|
||||||
randomPerson: Object.freeze<PersonEntity>({
|
|
||||||
id: 'person-3',
|
|
||||||
createdAt: new Date('2021-01-01'),
|
|
||||||
updatedAt: new Date('2021-01-01'),
|
|
||||||
ownerId: userStub.admin.id,
|
|
||||||
owner: userStub.admin,
|
|
||||||
name: '',
|
|
||||||
birthDate: null,
|
|
||||||
thumbnailPath: '/path/to/thumbnail',
|
|
||||||
faces: [],
|
|
||||||
faceAssetId: null,
|
|
||||||
faceAsset: null,
|
|
||||||
isHidden: false,
|
|
||||||
}),
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
import { quintOut } from 'svelte/easing';
|
import { quintOut } from 'svelte/easing';
|
||||||
import { fly } from 'svelte/transition';
|
import { fly } from 'svelte/transition';
|
||||||
import { createEventDispatcher } from 'svelte';
|
import { createEventDispatcher } from 'svelte';
|
||||||
import Slider from '$lib/components/elements/slider.svelte';
|
|
||||||
|
|
||||||
export let title: string;
|
export let title: string;
|
||||||
export let subtitle = '';
|
export let subtitle = '';
|
||||||
|
@ -11,6 +10,7 @@
|
||||||
export let isEdited = false;
|
export let isEdited = false;
|
||||||
|
|
||||||
const dispatch = createEventDispatcher<{ toggle: boolean }>();
|
const dispatch = createEventDispatcher<{ toggle: boolean }>();
|
||||||
|
const onToggle = (event: Event) => dispatch('toggle', (event.target as HTMLInputElement).checked);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="flex place-items-center justify-between">
|
<div class="flex place-items-center justify-between">
|
||||||
|
@ -31,5 +31,67 @@
|
||||||
|
|
||||||
<p class="text-sm dark:text-immich-dark-fg">{subtitle}</p>
|
<p class="text-sm dark:text-immich-dark-fg">{subtitle}</p>
|
||||||
</div>
|
</div>
|
||||||
<Slider bind:checked {disabled} on:toggle={() => dispatch('toggle', checked)} />
|
|
||||||
|
<label class="relative inline-block h-[10px] w-[36px] flex-none">
|
||||||
|
<input
|
||||||
|
class="disabled::cursor-not-allowed h-0 w-0 opacity-0"
|
||||||
|
type="checkbox"
|
||||||
|
bind:checked
|
||||||
|
on:click={onToggle}
|
||||||
|
{disabled}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{#if disabled}
|
||||||
|
<span class="slider slider-disabled cursor-not-allowed" />
|
||||||
|
{:else}
|
||||||
|
<span class="slider slider-enabled cursor-pointer" />
|
||||||
|
{/if}
|
||||||
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.slider {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background-color: #ccc;
|
||||||
|
-webkit-transition: 0.4s;
|
||||||
|
transition: 0.4s;
|
||||||
|
border-radius: 34px;
|
||||||
|
}
|
||||||
|
|
||||||
|
input:disabled {
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slider:before {
|
||||||
|
position: absolute;
|
||||||
|
content: '';
|
||||||
|
height: 20px;
|
||||||
|
width: 20px;
|
||||||
|
left: 0px;
|
||||||
|
right: 0px;
|
||||||
|
bottom: -4px;
|
||||||
|
background-color: gray;
|
||||||
|
-webkit-transition: 0.4s;
|
||||||
|
transition: 0.4s;
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
input:checked + .slider:before {
|
||||||
|
-webkit-transform: translateX(18px);
|
||||||
|
-ms-transform: translateX(18px);
|
||||||
|
transform: translateX(18px);
|
||||||
|
background-color: #4250af;
|
||||||
|
}
|
||||||
|
|
||||||
|
input:checked + .slider-disabled {
|
||||||
|
background-color: gray;
|
||||||
|
}
|
||||||
|
|
||||||
|
input:checked + .slider-enabled {
|
||||||
|
background-color: #adcbfa;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
|
@ -1,72 +0,0 @@
|
||||||
<script lang="ts">
|
|
||||||
import { createEventDispatcher } from 'svelte';
|
|
||||||
|
|
||||||
export let checked = false;
|
|
||||||
export let disabled = false;
|
|
||||||
|
|
||||||
const dispatch = createEventDispatcher<{ toggle: boolean }>();
|
|
||||||
const onToggle = (event: Event) => dispatch('toggle', (event.target as HTMLInputElement).checked);
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<label class="relative inline-block h-[10px] w-[36px] flex-none">
|
|
||||||
<input
|
|
||||||
class="disabled::cursor-not-allowed h-0 w-0 opacity-0"
|
|
||||||
type="checkbox"
|
|
||||||
bind:checked
|
|
||||||
on:click={onToggle}
|
|
||||||
{disabled}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{#if disabled}
|
|
||||||
<span class="slider slider-disabled cursor-not-allowed" />
|
|
||||||
{:else}
|
|
||||||
<span class="slider slider-enabled cursor-pointer" />
|
|
||||||
{/if}
|
|
||||||
</label>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.slider {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
bottom: 0;
|
|
||||||
background-color: #ccc;
|
|
||||||
-webkit-transition: 0.4s;
|
|
||||||
transition: 0.4s;
|
|
||||||
border-radius: 34px;
|
|
||||||
}
|
|
||||||
|
|
||||||
input:disabled {
|
|
||||||
cursor: not-allowed;
|
|
||||||
}
|
|
||||||
|
|
||||||
.slider:before {
|
|
||||||
position: absolute;
|
|
||||||
content: '';
|
|
||||||
height: 20px;
|
|
||||||
width: 20px;
|
|
||||||
left: 0px;
|
|
||||||
right: 0px;
|
|
||||||
bottom: -4px;
|
|
||||||
background-color: gray;
|
|
||||||
-webkit-transition: 0.4s;
|
|
||||||
transition: 0.4s;
|
|
||||||
border-radius: 50%;
|
|
||||||
}
|
|
||||||
|
|
||||||
input:checked + .slider:before {
|
|
||||||
-webkit-transform: translateX(18px);
|
|
||||||
-ms-transform: translateX(18px);
|
|
||||||
transform: translateX(18px);
|
|
||||||
background-color: #4250af;
|
|
||||||
}
|
|
||||||
|
|
||||||
input:checked + .slider-disabled {
|
|
||||||
background-color: gray;
|
|
||||||
}
|
|
||||||
|
|
||||||
input:checked + .slider-enabled {
|
|
||||||
background-color: #adcbfa;
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -26,7 +26,7 @@
|
||||||
|
|
||||||
let dispatch = createEventDispatcher<{
|
let dispatch = createEventDispatcher<{
|
||||||
back: void;
|
back: void;
|
||||||
merge: PersonResponseDto;
|
merge: void;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
$: hasSelection = selectedPeople.length > 0;
|
$: hasSelection = selectedPeople.length > 0;
|
||||||
|
@ -68,17 +68,16 @@
|
||||||
|
|
||||||
const handleMerge = async () => {
|
const handleMerge = async () => {
|
||||||
try {
|
try {
|
||||||
let { data: results } = await api.personApi.mergePerson({
|
const { data: results } = await api.personApi.mergePerson({
|
||||||
id: person.id,
|
id: person.id,
|
||||||
mergePersonDto: { ids: selectedPeople.map(({ id }) => id) },
|
mergePersonDto: { ids: selectedPeople.map(({ id }) => id) },
|
||||||
});
|
});
|
||||||
const { data: mergedPerson } = await api.personApi.getPerson({ id: person.id });
|
|
||||||
const count = results.filter(({ success }) => success).length;
|
const count = results.filter(({ success }) => success).length;
|
||||||
notificationController.show({
|
notificationController.show({
|
||||||
message: `Merged ${count} ${count === 1 ? 'person' : 'people'}`,
|
message: `Merged ${count} ${count === 1 ? 'person' : 'people'}`,
|
||||||
type: NotificationType.Info,
|
type: NotificationType.Info,
|
||||||
});
|
});
|
||||||
dispatch('merge', mergedPerson);
|
dispatch('merge');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
handleError(error, 'Cannot merge people');
|
handleError(error, 'Cannot merge people');
|
||||||
} finally {
|
} finally {
|
||||||
|
|
|
@ -165,12 +165,8 @@
|
||||||
id: personMerge2.id,
|
id: personMerge2.id,
|
||||||
mergePersonDto: { ids: [personToMerge.id] },
|
mergePersonDto: { ids: [personToMerge.id] },
|
||||||
});
|
});
|
||||||
|
|
||||||
const { data: mergedPerson } = await api.personApi.getPerson({ id: personToMerge.id });
|
|
||||||
|
|
||||||
countVisiblePeople--;
|
countVisiblePeople--;
|
||||||
people = people.filter((person: PersonResponseDto) => person.id !== personToMerge.id);
|
people = people.filter((person: PersonResponseDto) => person.id !== personToMerge.id);
|
||||||
people = people.map((person: PersonResponseDto) => (person.id === personMerge2.id ? mergedPerson : person));
|
|
||||||
|
|
||||||
notificationController.show({
|
notificationController.show({
|
||||||
message: 'Merge people succesfully',
|
message: 'Merge people succesfully',
|
||||||
|
|
|
@ -185,13 +185,8 @@
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleMerge = async (person: PersonResponseDto) => {
|
const handleMerge = () => {
|
||||||
const { data: statistics } = await api.personApi.getPersonStatistics({ id: person.id });
|
|
||||||
numberOfAssets = statistics.assets;
|
|
||||||
handleGoBack();
|
handleGoBack();
|
||||||
|
|
||||||
data.person = person;
|
|
||||||
|
|
||||||
refreshAssetGrid = !refreshAssetGrid;
|
refreshAssetGrid = !refreshAssetGrid;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -379,7 +374,7 @@
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
{#if viewMode === ViewMode.MERGE_PEOPLE}
|
{#if viewMode === ViewMode.MERGE_PEOPLE}
|
||||||
<MergeFaceSelector person={data.person} on:back={handleGoBack} on:merge={({ detail }) => handleMerge(detail)} />
|
<MergeFaceSelector person={data.person} on:back={handleGoBack} on:merge={handleMerge} />
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<header>
|
<header>
|
||||||
|
|
Loading…
Reference in a new issue