|
290 | 290 | <v-list-item class="metadata__item meta-labels"> |
291 | 291 | <div class="text-subtitle-2">{{ $gettext("Labels") }}</div> |
292 | 292 | <template v-if="isEditable && chipState.labels.removals.length > 0" #append> |
293 | | - <p-sidebar-inline-toolbar :editing="true" :can-undo="true" @confirm="confirmLabels" @undo="undoChipRemovals('labels')" /> |
| 293 | + <p-sidebar-inline-toolbar :editing="true" :can-undo="true" chip-mode @confirm="confirmLabels" @undo="undoChipRemovals('labels')" /> |
294 | 294 | </template> |
295 | 295 | </v-list-item> |
296 | 296 | <v-list-item v-if="visibleLabels.length > 0" class="metadata__item metadata__chips meta-labels"> |
|
350 | 350 | <v-list-item class="metadata__item meta-albums"> |
351 | 351 | <div class="text-subtitle-2">{{ $gettext("Albums") }}</div> |
352 | 352 | <template v-if="isEditable && chipState.albums.removals.length > 0" #append> |
353 | | - <p-sidebar-inline-toolbar :editing="true" :can-undo="true" @confirm="confirmAlbums" @undo="undoChipRemovals('albums')" /> |
| 353 | + <p-sidebar-inline-toolbar :editing="true" :can-undo="true" chip-mode @confirm="confirmAlbums" @undo="undoChipRemovals('albums')" /> |
354 | 354 | </template> |
355 | 355 | </v-list-item> |
356 | 356 | <v-list-item v-if="visibleAlbums.length > 0" class="metadata__item metadata__chips meta-albums"> |
@@ -1729,8 +1729,22 @@ export default { |
1729 | 1729 | } |
1730 | 1730 | const norm = this.$util.normalizeTitle(name); |
1731 | 1731 | if (!norm) return false; |
1732 | | - // Already on the photo? Skip the API call. |
1733 | | - if (this.labels.some((l) => this.$util.normalizeTitle(l?.Label?.Name) === norm)) return false; |
| 1732 | + // If the typed label matches a chip currently pending removal, |
| 1733 | + // restore it instead of calling the API. Without this branch the |
| 1734 | + // `visibleLabels.some` check below would still let the call through |
| 1735 | + // (the chip is filtered out of visibleLabels) but the resulting |
| 1736 | + // POST would race the deferred DELETE on auto-commit and leave the |
| 1737 | + // photo in an unpredictable state — and intuitively "type the name |
| 1738 | + // you just × clicked" should mean "put it back", not "round-trip". |
| 1739 | + const pending = this.labels.find( |
| 1740 | + (l) => this.isChipPendingRemoval("labels", l?.Label?.ID) && this.$util.normalizeTitle(l?.Label?.Name) === norm |
| 1741 | + ); |
| 1742 | + if (pending?.Label?.ID != null) { |
| 1743 | + this.togglePendingChipRemoval("labels", pending.Label.ID); |
| 1744 | + return true; |
| 1745 | + } |
| 1746 | + // Already on the photo (and not pending removal)? Skip the API call. |
| 1747 | + if (this.visibleLabels.some((l) => this.$util.normalizeTitle(l?.Label?.Name) === norm)) return false; |
1734 | 1748 | // Match against the system-wide label list — if a normalized match |
1735 | 1749 | // exists, send the canonical existing-label name so the backend |
1736 | 1750 | // doesn't create a near-duplicate (e.g. typed `Hello Cat` reuses an |
@@ -1760,6 +1774,13 @@ export default { |
1760 | 1774 | this.$notify.error(this.$gettext("Name too long")); |
1761 | 1775 | return false; |
1762 | 1776 | } |
| 1777 | + // If the album is pending removal, restore it instead of calling |
| 1778 | + // the API. Mirrors the Labels pending-restore path; see |
| 1779 | + // addLabelImmediate for the rationale. |
| 1780 | + if (this.isChipPendingRemoval("albums", album.UID)) { |
| 1781 | + this.togglePendingChipRemoval("albums", album.UID); |
| 1782 | + return true; |
| 1783 | + } |
1763 | 1784 | if (this.albums.some((a) => a.UID === album.UID)) return false; |
1764 | 1785 | const norm = this.$util.normalizeTitle(title); |
1765 | 1786 | if (this.albumTitleConflicts(norm)) return false; |
|
0 commit comments