mirror of
				https://github.com/immich-app/immich.git
				synced 2025-11-03 19:17:11 -05:00 
			
		
		
		
	fix(web): notification number of people when editing faces (#7352)
* fix: notification number of people when editing faces * fix: lint * fix: use id instead of index * rename
This commit is contained in:
		
							parent
							
								
									d2b5cc6a4a
								
							
						
					
					
						commit
						efd8f0d648
					
				@ -21,7 +21,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  export let peopleWithFaces: AssetFaceResponseDto[];
 | 
					  export let peopleWithFaces: AssetFaceResponseDto[];
 | 
				
			||||||
  export let allPeople: PersonResponseDto[];
 | 
					  export let allPeople: PersonResponseDto[];
 | 
				
			||||||
  export let editedPersonIndex: number;
 | 
					  export let editedPerson: PersonResponseDto;
 | 
				
			||||||
  export let assetType: AssetTypeEnum;
 | 
					  export let assetType: AssetTypeEnum;
 | 
				
			||||||
  export let assetId: string;
 | 
					  export let assetId: string;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -106,7 +106,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  const handleCreatePerson = async () => {
 | 
					  const handleCreatePerson = async () => {
 | 
				
			||||||
    const timeout = setTimeout(() => (isShowLoadingNewPerson = true), timeBeforeShowLoadingSpinner);
 | 
					    const timeout = setTimeout(() => (isShowLoadingNewPerson = true), timeBeforeShowLoadingSpinner);
 | 
				
			||||||
    const personToUpdate = peopleWithFaces.find((person) => person.id === peopleWithFaces[editedPersonIndex].id);
 | 
					    const personToUpdate = peopleWithFaces.find((face) => face.person?.id === editedPerson.id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const newFeaturePhoto = personToUpdate ? await zoomImageToBase64(personToUpdate) : null;
 | 
					    const newFeaturePhoto = personToUpdate ? await zoomImageToBase64(personToUpdate) : null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -229,7 +229,7 @@
 | 
				
			|||||||
    <div class="immich-scrollbar mt-4 flex flex-wrap gap-2 overflow-y-auto">
 | 
					    <div class="immich-scrollbar mt-4 flex flex-wrap gap-2 overflow-y-auto">
 | 
				
			||||||
      {#if searchName == ''}
 | 
					      {#if searchName == ''}
 | 
				
			||||||
        {#each allPeople as person (person.id)}
 | 
					        {#each allPeople as person (person.id)}
 | 
				
			||||||
          {#if person.id !== peopleWithFaces[editedPersonIndex].person?.id}
 | 
					          {#if person.id !== editedPerson.id}
 | 
				
			||||||
            <div class="w-fit">
 | 
					            <div class="w-fit">
 | 
				
			||||||
              <button class="w-[90px]" on:click={() => dispatch('reassign', person)}>
 | 
					              <button class="w-[90px]" on:click={() => dispatch('reassign', person)}>
 | 
				
			||||||
                <div class="relative">
 | 
					                <div class="relative">
 | 
				
			||||||
@ -255,7 +255,7 @@
 | 
				
			|||||||
        {/each}
 | 
					        {/each}
 | 
				
			||||||
      {:else}
 | 
					      {:else}
 | 
				
			||||||
        {#each searchedPeople as person (person.id)}
 | 
					        {#each searchedPeople as person (person.id)}
 | 
				
			||||||
          {#if person.id !== peopleWithFaces[editedPersonIndex].person?.id}
 | 
					          {#if person.id !== editedPerson.id}
 | 
				
			||||||
            <div class="w-fit">
 | 
					            <div class="w-fit">
 | 
				
			||||||
              <button class="w-[90px]" on:click={() => dispatch('reassign', person)}>
 | 
					              <button class="w-[90px]" on:click={() => dispatch('reassign', person)}>
 | 
				
			||||||
                <div class="relative">
 | 
					                <div class="relative">
 | 
				
			||||||
 | 
				
			|||||||
@ -28,14 +28,14 @@
 | 
				
			|||||||
  export let assetType: AssetTypeEnum;
 | 
					  export let assetType: AssetTypeEnum;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // keep track of the changes
 | 
					  // keep track of the changes
 | 
				
			||||||
  let numberOfPersonToCreate: string[] = [];
 | 
					  let peopleToCreate: string[] = [];
 | 
				
			||||||
  let numberOfAssetFaceGenerated: string[] = [];
 | 
					  let assetFaceGenerated: string[] = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // faces
 | 
					  // faces
 | 
				
			||||||
  let peopleWithFaces: AssetFaceResponseDto[] = [];
 | 
					  let peopleWithFaces: AssetFaceResponseDto[] = [];
 | 
				
			||||||
  let selectedPersonToReassign: (PersonResponseDto | null)[];
 | 
					  let selectedPersonToReassign: Record<string, PersonResponseDto> = {};
 | 
				
			||||||
  let selectedPersonToCreate: (string | null)[];
 | 
					  let selectedPersonToCreate: Record<string, string> = {};
 | 
				
			||||||
  let editedPersonIndex: number;
 | 
					  let editedPerson: PersonResponseDto;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // loading spinners
 | 
					  // loading spinners
 | 
				
			||||||
  let isShowLoadingDone = false;
 | 
					  let isShowLoadingDone = false;
 | 
				
			||||||
@ -49,6 +49,8 @@
 | 
				
			|||||||
  let loaderLoadingDoneTimeout: ReturnType<typeof setTimeout>;
 | 
					  let loaderLoadingDoneTimeout: ReturnType<typeof setTimeout>;
 | 
				
			||||||
  let automaticRefreshTimeout: ReturnType<typeof setTimeout>;
 | 
					  let automaticRefreshTimeout: ReturnType<typeof setTimeout>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const thumbnailWidth = '90px';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const dispatch = createEventDispatcher<{
 | 
					  const dispatch = createEventDispatcher<{
 | 
				
			||||||
    close: void;
 | 
					    close: void;
 | 
				
			||||||
    refresh: void;
 | 
					    refresh: void;
 | 
				
			||||||
@ -60,8 +62,6 @@
 | 
				
			|||||||
      const { people } = await getAllPeople({ withHidden: true });
 | 
					      const { people } = await getAllPeople({ withHidden: true });
 | 
				
			||||||
      allPeople = people;
 | 
					      allPeople = people;
 | 
				
			||||||
      peopleWithFaces = await getFaces({ id: assetId });
 | 
					      peopleWithFaces = await getFaces({ id: assetId });
 | 
				
			||||||
      selectedPersonToCreate = Array.from({ length: peopleWithFaces.length });
 | 
					 | 
				
			||||||
      selectedPersonToReassign = Array.from({ length: peopleWithFaces.length });
 | 
					 | 
				
			||||||
    } catch (error) {
 | 
					    } catch (error) {
 | 
				
			||||||
      handleError(error, "Can't get faces");
 | 
					      handleError(error, "Can't get faces");
 | 
				
			||||||
    } finally {
 | 
					    } finally {
 | 
				
			||||||
@ -71,12 +71,12 @@
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const onPersonThumbnail = (personId: string) => {
 | 
					  const onPersonThumbnail = (personId: string) => {
 | 
				
			||||||
    numberOfAssetFaceGenerated.push(personId);
 | 
					    assetFaceGenerated.push(personId);
 | 
				
			||||||
    if (
 | 
					    if (
 | 
				
			||||||
      isEqual(numberOfAssetFaceGenerated, numberOfPersonToCreate) &&
 | 
					      isEqual(assetFaceGenerated, peopleToCreate) &&
 | 
				
			||||||
      loaderLoadingDoneTimeout &&
 | 
					      loaderLoadingDoneTimeout &&
 | 
				
			||||||
      automaticRefreshTimeout &&
 | 
					      automaticRefreshTimeout &&
 | 
				
			||||||
      selectedPersonToCreate.filter((person) => person !== null).length === numberOfPersonToCreate.length
 | 
					      Object.keys(selectedPersonToCreate).length === peopleToCreate.length
 | 
				
			||||||
    ) {
 | 
					    ) {
 | 
				
			||||||
      clearTimeout(loaderLoadingDoneTimeout);
 | 
					      clearTimeout(loaderLoadingDoneTimeout);
 | 
				
			||||||
      clearTimeout(automaticRefreshTimeout);
 | 
					      clearTimeout(automaticRefreshTimeout);
 | 
				
			||||||
@ -97,36 +97,41 @@
 | 
				
			|||||||
    dispatch('close');
 | 
					    dispatch('close');
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const handleReset = (index: number) => {
 | 
					  const handleReset = (id: string) => {
 | 
				
			||||||
    if (selectedPersonToReassign[index]) {
 | 
					    if (selectedPersonToReassign[id]) {
 | 
				
			||||||
      selectedPersonToReassign[index] = null;
 | 
					      delete selectedPersonToReassign[id];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      // trigger reactivity
 | 
				
			||||||
 | 
					      selectedPersonToReassign = selectedPersonToReassign;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    if (selectedPersonToCreate[index]) {
 | 
					    if (selectedPersonToCreate[id]) {
 | 
				
			||||||
      selectedPersonToCreate[index] = null;
 | 
					      delete selectedPersonToCreate[id];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      // trigger reactivity
 | 
				
			||||||
 | 
					      selectedPersonToCreate = selectedPersonToCreate;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const handleEditFaces = async () => {
 | 
					  const handleEditFaces = async () => {
 | 
				
			||||||
    loaderLoadingDoneTimeout = setTimeout(() => (isShowLoadingDone = true), timeBeforeShowLoadingSpinner);
 | 
					    loaderLoadingDoneTimeout = setTimeout(() => (isShowLoadingDone = true), timeBeforeShowLoadingSpinner);
 | 
				
			||||||
    const numberOfChanges =
 | 
					    const numberOfChanges = Object.keys(selectedPersonToCreate).length + Object.keys(selectedPersonToReassign).length;
 | 
				
			||||||
      selectedPersonToCreate.filter((person) => person !== null).length +
 | 
					
 | 
				
			||||||
      selectedPersonToReassign.filter((person) => person !== null).length;
 | 
					 | 
				
			||||||
    if (numberOfChanges > 0) {
 | 
					    if (numberOfChanges > 0) {
 | 
				
			||||||
      try {
 | 
					      try {
 | 
				
			||||||
        for (const [index, peopleWithFace] of peopleWithFaces.entries()) {
 | 
					        for (const personWithFace of peopleWithFaces) {
 | 
				
			||||||
          const personId = selectedPersonToReassign[index]?.id;
 | 
					          const personId = selectedPersonToReassign[personWithFace.id]?.id;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
          if (personId) {
 | 
					          if (personId) {
 | 
				
			||||||
            await reassignFacesById({
 | 
					            await reassignFacesById({
 | 
				
			||||||
              id: personId,
 | 
					              id: personId,
 | 
				
			||||||
              faceDto: { id: peopleWithFace.id },
 | 
					              faceDto: { id: personWithFace.id },
 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
          } else if (selectedPersonToCreate[index]) {
 | 
					          } else if (selectedPersonToCreate[personWithFace.id]) {
 | 
				
			||||||
            const data = await createPerson({ personCreateDto: {} });
 | 
					            const data = await createPerson({ personCreateDto: {} });
 | 
				
			||||||
            numberOfPersonToCreate.push(data.id);
 | 
					            peopleToCreate.push(data.id);
 | 
				
			||||||
            await reassignFacesById({
 | 
					            await reassignFacesById({
 | 
				
			||||||
              id: data.id,
 | 
					              id: data.id,
 | 
				
			||||||
              faceDto: { id: peopleWithFace.id },
 | 
					              faceDto: { id: personWithFace.id },
 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@ -141,7 +146,7 @@
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    isShowLoadingDone = false;
 | 
					    isShowLoadingDone = false;
 | 
				
			||||||
    if (numberOfPersonToCreate.length === 0) {
 | 
					    if (peopleToCreate.length === 0) {
 | 
				
			||||||
      clearTimeout(loaderLoadingDoneTimeout);
 | 
					      clearTimeout(loaderLoadingDoneTimeout);
 | 
				
			||||||
      dispatch('refresh');
 | 
					      dispatch('refresh');
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
@ -150,23 +155,26 @@
 | 
				
			|||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const handleCreatePerson = (newFeaturePhoto: string | null) => {
 | 
					  const handleCreatePerson = (newFeaturePhoto: string | null) => {
 | 
				
			||||||
    const personToUpdate = peopleWithFaces.find((person) => person.id === peopleWithFaces[editedPersonIndex].id);
 | 
					    const personToUpdate = peopleWithFaces.find((face) => face.person?.id === editedPerson.id);
 | 
				
			||||||
    if (newFeaturePhoto && personToUpdate) {
 | 
					    if (newFeaturePhoto && personToUpdate) {
 | 
				
			||||||
      selectedPersonToCreate[peopleWithFaces.indexOf(personToUpdate)] = newFeaturePhoto;
 | 
					      selectedPersonToCreate[personToUpdate.id] = newFeaturePhoto;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    showSeletecFaces = false;
 | 
					    showSeletecFaces = false;
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const handleReassignFace = (person: PersonResponseDto | null) => {
 | 
					  const handleReassignFace = (person: PersonResponseDto | null) => {
 | 
				
			||||||
    if (person) {
 | 
					    const personToUpdate = peopleWithFaces.find((face) => face.person?.id === editedPerson.id);
 | 
				
			||||||
      selectedPersonToReassign[editedPersonIndex] = person;
 | 
					    if (person && personToUpdate) {
 | 
				
			||||||
 | 
					      selectedPersonToReassign[personToUpdate.id] = person;
 | 
				
			||||||
      showSeletecFaces = false;
 | 
					      showSeletecFaces = false;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const handlePersonPicker = (index: number) => {
 | 
					  const handlePersonPicker = (person: PersonResponseDto | null) => {
 | 
				
			||||||
    editedPersonIndex = index;
 | 
					    if (person) {
 | 
				
			||||||
    showSeletecFaces = true;
 | 
					      editedPerson = person;
 | 
				
			||||||
 | 
					      showSeletecFaces = true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -217,35 +225,48 @@
 | 
				
			|||||||
                on:mouseleave={() => ($boundingBoxesArray = [])}
 | 
					                on:mouseleave={() => ($boundingBoxesArray = [])}
 | 
				
			||||||
              >
 | 
					              >
 | 
				
			||||||
                <div class="relative">
 | 
					                <div class="relative">
 | 
				
			||||||
                  <ImageThumbnail
 | 
					                  {#if selectedPersonToCreate[face.id]}
 | 
				
			||||||
                    curve
 | 
					                    <ImageThumbnail
 | 
				
			||||||
                    shadow
 | 
					                      curve
 | 
				
			||||||
                    url={selectedPersonToCreate[index] ||
 | 
					                      shadow
 | 
				
			||||||
                      getPeopleThumbnailUrl(selectedPersonToReassign[index]?.id || face.person.id)}
 | 
					                      url={selectedPersonToCreate[face.id]}
 | 
				
			||||||
                    altText={selectedPersonToReassign[index]
 | 
					                      altText={selectedPersonToCreate[face.id]}
 | 
				
			||||||
                      ? selectedPersonToReassign[index]?.name
 | 
					                      title={'New person'}
 | 
				
			||||||
                      : selectedPersonToCreate[index]
 | 
					                      widthStyle={thumbnailWidth}
 | 
				
			||||||
                        ? 'New person'
 | 
					                      heightStyle={thumbnailWidth}
 | 
				
			||||||
                        : getPersonNameWithHiddenValue(face.person?.name, face.person?.isHidden)}
 | 
					                    />
 | 
				
			||||||
                    title={selectedPersonToReassign[index]
 | 
					                  {:else if selectedPersonToReassign[face.id]}
 | 
				
			||||||
                      ? selectedPersonToReassign[index]?.name
 | 
					                    <ImageThumbnail
 | 
				
			||||||
                      : selectedPersonToCreate[index]
 | 
					                      curve
 | 
				
			||||||
                        ? 'New person'
 | 
					                      shadow
 | 
				
			||||||
                        : getPersonNameWithHiddenValue(face.person?.name, face.person?.isHidden)}
 | 
					                      url={getPeopleThumbnailUrl(selectedPersonToReassign[face.id].id)}
 | 
				
			||||||
                    widthStyle="90px"
 | 
					                      altText={selectedPersonToReassign[face.id]?.name || selectedPersonToReassign[face.id].id}
 | 
				
			||||||
                    heightStyle="90px"
 | 
					                      title={getPersonNameWithHiddenValue(
 | 
				
			||||||
                    thumbhash={null}
 | 
					                        selectedPersonToReassign[face.id].name,
 | 
				
			||||||
                    hidden={selectedPersonToReassign[index]
 | 
					                        face.person?.isHidden,
 | 
				
			||||||
                      ? selectedPersonToReassign[index]?.isHidden
 | 
					                      )}
 | 
				
			||||||
                      : selectedPersonToCreate[index]
 | 
					                      widthStyle={thumbnailWidth}
 | 
				
			||||||
                        ? false
 | 
					                      heightStyle={thumbnailWidth}
 | 
				
			||||||
                        : face.person?.isHidden}
 | 
					                      hidden={selectedPersonToReassign[face.id].isHidden}
 | 
				
			||||||
                  />
 | 
					                    />
 | 
				
			||||||
 | 
					                  {:else}
 | 
				
			||||||
 | 
					                    <ImageThumbnail
 | 
				
			||||||
 | 
					                      curve
 | 
				
			||||||
 | 
					                      shadow
 | 
				
			||||||
 | 
					                      url={getPeopleThumbnailUrl(face.person.id)}
 | 
				
			||||||
 | 
					                      altText={face.person.name || face.person.id}
 | 
				
			||||||
 | 
					                      title={getPersonNameWithHiddenValue(face.person.name, face.person.isHidden)}
 | 
				
			||||||
 | 
					                      widthStyle={thumbnailWidth}
 | 
				
			||||||
 | 
					                      heightStyle={thumbnailWidth}
 | 
				
			||||||
 | 
					                      hidden={face.person.isHidden}
 | 
				
			||||||
 | 
					                    />
 | 
				
			||||||
 | 
					                  {/if}
 | 
				
			||||||
                </div>
 | 
					                </div>
 | 
				
			||||||
                {#if !selectedPersonToCreate[index]}
 | 
					
 | 
				
			||||||
 | 
					                {#if !selectedPersonToCreate[face.id]}
 | 
				
			||||||
                  <p class="relative mt-1 truncate font-medium" title={face.person?.name}>
 | 
					                  <p class="relative mt-1 truncate font-medium" title={face.person?.name}>
 | 
				
			||||||
                    {#if selectedPersonToReassign[index]?.id}
 | 
					                    {#if selectedPersonToReassign[face.id]?.id}
 | 
				
			||||||
                      {selectedPersonToReassign[index]?.name}
 | 
					                      {selectedPersonToReassign[face.id]?.name}
 | 
				
			||||||
                    {:else}
 | 
					                    {:else}
 | 
				
			||||||
                      {face.person?.name}
 | 
					                      {face.person?.name}
 | 
				
			||||||
                    {/if}
 | 
					                    {/if}
 | 
				
			||||||
@ -253,8 +274,8 @@
 | 
				
			|||||||
                {/if}
 | 
					                {/if}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                <div class="absolute -right-[5px] -top-[5px] h-[20px] w-[20px] rounded-full bg-blue-700">
 | 
					                <div class="absolute -right-[5px] -top-[5px] h-[20px] w-[20px] rounded-full bg-blue-700">
 | 
				
			||||||
                  {#if selectedPersonToCreate[index] || selectedPersonToReassign[index]}
 | 
					                  {#if selectedPersonToCreate[face.id] || selectedPersonToReassign[face.id]}
 | 
				
			||||||
                    <button on:click={() => handleReset(index)} class="flex h-full w-full">
 | 
					                    <button on:click={() => handleReset(face.id)} class="flex h-full w-full">
 | 
				
			||||||
                      <div class="absolute left-1/2 top-1/2 translate-x-[-50%] translate-y-[-50%] transform">
 | 
					                      <div class="absolute left-1/2 top-1/2 translate-x-[-50%] translate-y-[-50%] transform">
 | 
				
			||||||
                        <div>
 | 
					                        <div>
 | 
				
			||||||
                          <Icon path={mdiRestart} size={18} />
 | 
					                          <Icon path={mdiRestart} size={18} />
 | 
				
			||||||
@ -262,7 +283,7 @@
 | 
				
			|||||||
                      </div>
 | 
					                      </div>
 | 
				
			||||||
                    </button>
 | 
					                    </button>
 | 
				
			||||||
                  {:else}
 | 
					                  {:else}
 | 
				
			||||||
                    <button on:click={() => handlePersonPicker(index)} class="flex h-full w-full">
 | 
					                    <button on:click={() => handlePersonPicker(face.person)} class="flex h-full w-full">
 | 
				
			||||||
                      <div
 | 
					                      <div
 | 
				
			||||||
                        class="absolute left-1/2 top-1/2 h-[2px] w-[14px] translate-x-[-50%] translate-y-[-50%] transform bg-white"
 | 
					                        class="absolute left-1/2 top-1/2 h-[2px] w-[14px] translate-x-[-50%] translate-y-[-50%] transform bg-white"
 | 
				
			||||||
                      />
 | 
					                      />
 | 
				
			||||||
@ -282,7 +303,7 @@
 | 
				
			|||||||
  <AssignFaceSidePanel
 | 
					  <AssignFaceSidePanel
 | 
				
			||||||
    {peopleWithFaces}
 | 
					    {peopleWithFaces}
 | 
				
			||||||
    {allPeople}
 | 
					    {allPeople}
 | 
				
			||||||
    {editedPersonIndex}
 | 
					    {editedPerson}
 | 
				
			||||||
    {assetType}
 | 
					    {assetType}
 | 
				
			||||||
    {assetId}
 | 
					    {assetId}
 | 
				
			||||||
    on:close={() => (showSeletecFaces = false)}
 | 
					    on:close={() => (showSeletecFaces = false)}
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user