From 1e19437494ebf5a6a2a472d90ceaf4fb1d57d01f Mon Sep 17 00:00:00 2001 From: DIGI Byte <6645396+digimbyte@users.noreply.github.com> Date: Wed, 2 Sep 2026 18:11:01 +1000 Subject: [PATCH 01/14] Fix annotation composer and identity label layout --- src/components/PublicGuestDialog.vue | 2 +- src/components/PublicLightboxAnnotations.vue | 4 ++-- tests/e2e/gallery.spec.ts | 16 +++++++++++++++- 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/src/components/PublicGuestDialog.vue b/src/components/PublicGuestDialog.vue index 513c8d1..9c4038c 100644 --- a/src/components/PublicGuestDialog.vue +++ b/src/components/PublicGuestDialog.vue @@ -70,5 +70,5 @@ defineEmits<{ dismiss: []; submit: [] }>() .guest-dialog__form ion-item { --background: var(--gallery-surface); --border-color: var(--gallery-border); --color: var(--gallery-text); --min-height: 62px; } -.guest-dialog__form > ion-button { min-height: 46px; margin: 0; --border-radius: 9px; text-transform: none; } +.guest-dialog__form > ion-button { min-height: 46px; margin: 0; --background: var(--gallery-accent); --border-radius: 9px; --color: var(--ion-color-primary-contrast, #fff); text-transform: none; } diff --git a/src/components/PublicLightboxAnnotations.vue b/src/components/PublicLightboxAnnotations.vue index 2bb671e..c3cb65a 100644 --- a/src/components/PublicLightboxAnnotations.vue +++ b/src/components/PublicLightboxAnnotations.vue @@ -120,9 +120,9 @@ function onComposerKeydown(event: KeyboardEvent) { .annotation-positioning { position: fixed; z-index: 100120; inset: auto 50% 24px auto; margin: 0; padding: 10px 16px; border-radius: 999px; background: rgb(24 24 27 / 96%); box-shadow: 0 8px 28px rgb(0 0 0 / 35%); color: #fff; font-size: 13px; pointer-events: none; transform: translateX(50%); } -.annotation-composer { position: fixed; z-index: 100120; display: grid; width: min(360px, calc(100vw - 32px)); gap: 10px; padding: 16px; border: 1px solid rgb(255 255 255 / 16%); border-radius: 14px; background: rgb(24 24 27 / 98%); box-shadow: 0 18px 50px rgb(0 0 0 / 48%); color: #fff; pointer-events: auto; transform: translate(var(--annotation-composer-offset, 18px), -18px); } +.annotation-composer { position: fixed; z-index: 100120; display: grid; box-sizing: border-box; width: min(360px, calc(100vw - 32px)); max-height: calc(100dvh - 32px); overflow-y: auto; gap: 10px; padding: 16px; border: 1px solid rgb(255 255 255 / 16%); border-radius: 14px; background: rgb(24 24 27 / 98%); box-shadow: 0 18px 50px rgb(0 0 0 / 48%); color: #fff; overscroll-behavior: contain; pointer-events: auto; transform: translate(var(--annotation-composer-offset, 18px), -18px); } -.annotation-composer textarea { min-height: 96px; padding: 10px 12px; border: 1px solid rgb(255 255 255 / 24%); border-radius: 8px; background: #fff; color: #111; font: inherit; resize: vertical; } +.annotation-composer textarea { box-sizing: border-box; width: 100%; min-width: 0; max-width: 100%; min-height: 96px; padding: 10px 12px; border: 1px solid rgb(255 255 255 / 24%); border-radius: 8px; background: #fff; color: #111; font: inherit; resize: vertical; } .annotation-composer > div { display: flex; gap: 8px; } diff --git a/tests/e2e/gallery.spec.ts b/tests/e2e/gallery.spec.ts index 98a6798..97e522e 100644 --- a/tests/e2e/gallery.spec.ts +++ b/tests/e2e/gallery.spec.ts @@ -416,10 +416,24 @@ test('guest completes an accessible proofing flow', async ({ page, baseURL }) => const imageBounds = await image.boundingBox() expect(imageBounds).not.toBeNull() await image.click({ position: { x: imageBounds!.width * 0.67, y: imageBounds!.height * 0.42 } }) + const composer = page.locator('.annotation-composer') + const composerBounds = await composer.boundingBox() + const textareaBounds = await page.getByRole('textbox', { name: 'Point comment' }).boundingBox() + const viewport = page.viewportSize() + expect(composerBounds).not.toBeNull() + expect(textareaBounds).not.toBeNull() + expect(viewport).not.toBeNull() + expect(composerBounds!.x).toBeGreaterThanOrEqual(0) + expect(composerBounds!.x + composerBounds!.width).toBeLessThanOrEqual(viewport!.width) + expect(textareaBounds!.x).toBeGreaterThanOrEqual(composerBounds!.x) + expect(textareaBounds!.x + textareaBounds!.width).toBeLessThanOrEqual(composerBounds!.x + composerBounds!.width) await page.getByRole('textbox', { name: 'Point comment' }).fill('Retouch this point') await page.getByRole('button', { name: 'Comment', exact: true }).click() await page.getByRole('textbox', { name: 'Your name' }).fill('Playwright Reviewer') - await page.getByRole('button', { name: 'Continue' }).click() + const identityButton = page.getByRole('button', { name: 'Continue' }) + await expect(identityButton).toContainText('Continue') + await expect(identityButton).toHaveCSS('--color', '#ffffff') + await identityButton.click() await expect(page.getByRole('textbox', { name: 'Your name' })).toHaveCount(0) const pointMarker = page.getByRole('button', { name: 'Open point comment 1' }) await expect(pointMarker).toBeVisible() From a23dbc2f71b113641bad8681502c9be613740c50 Mon Sep 17 00:00:00 2001 From: DIGI Byte <6645396+digimbyte@users.noreply.github.com> Date: Wed, 2 Sep 2026 19:16:25 +1000 Subject: [PATCH 02/14] Fix annotation feedback filtering and pin layout --- l10n/de.js | 3 + l10n/de.json | 3 + l10n/en.js | 3 + l10n/en.json | 3 + lib/Dto/Settings/ReviewSettings.php | 2 +- src/components/PublicLightbox.vue | 72 ++++++-------- src/components/PublicLightboxAnnotations.vue | 2 +- src/components/PublicLightboxComments.spec.ts | 40 ++++++++ src/components/PublicLightboxComments.vue | 97 +++++++++++++++++++ src/components/styles/PublicLightbox.css | 36 +++---- src/domain/gallerySettings.spec.ts | 2 +- src/domain/gallerySettings.ts | 8 +- src/domain/lightboxReview.spec.ts | 13 ++- src/domain/lightboxReview.ts | 8 ++ tests/Unit/Dto/GallerySettingsTest.php | 2 +- tests/e2e/gallery.spec.ts | 14 +++ 16 files changed, 237 insertions(+), 71 deletions(-) create mode 100644 src/components/PublicLightboxComments.spec.ts create mode 100644 src/components/PublicLightboxComments.vue diff --git a/l10n/de.js b/l10n/de.js index daff44f..ad1a643 100644 --- a/l10n/de.js +++ b/l10n/de.js @@ -130,6 +130,7 @@ OC.L10N.register("proofing_gallery", { "All activity": "Alle Aktivitäten", "All changes are saved": "Alle Änderungen sind gespeichert", "All collection files are available": "Alle Dateien der Sammlung sind verfügbar", + "All feedback": "Gesamtes Feedback", "All galleries": "Alle Galerien", "All photos": "Alle Fotos", "All source files available": "Alle Quelldateien verfügbar", @@ -525,6 +526,7 @@ OC.L10N.register("proofing_gallery", { "Gallery updates for “%s”": "Aktualisierungen für „%s“", "Gallery view": "Galerieansicht", "General": "Allgemein", + "General comment": "Allgemeiner Kommentar", "Generated locally in your browser. The gallery link is not sent to a third party.": "Wird lokal in Ihrem Browser erstellt. Der Galerielink wird nicht an Dritte gesendet.", "Give a Nextcloud user or group view-only or editing access.": "Geben Sie einer Nextcloud-Person oder -Gruppe Lese- oder Bearbeitungszugriff.", "Give every audience its own URL, permissions and folder scope. No password or token is written to the audit log.": "Gib jeder Zielgruppe eine eigene URL, eigene Berechtigungen und einen eigenen Ordnerbereich. Passwörter und Token werden nie im Auditprotokoll gespeichert.", @@ -1059,6 +1061,7 @@ OC.L10N.register("proofing_gallery", { "Select all visible files": "Alle sichtbaren Dateien auswählen", "Select photos across pages, then download them together.": "Fotos seitenübergreifend auswählen und anschließend gemeinsam herunterladen.", "Select public image information to make this panel available to guests.": "Wählen Sie öffentliche Bildinformationen aus, damit dieses Panel Gästen zur Verfügung steht.", + "Selected annotation": "Ausgewählte Anmerkung", "Selected file actions": "Aktionen für ausgewählte Dateien", "Selected files": "Ausgewählte Dateien", "Selected information appears in the photo viewer. Changing this selection opens that preview scene.": "Ausgewählte Informationen erscheinen in der Fotoansicht. Änderungen an dieser Auswahl öffnen die entsprechende Vorschauszene.", diff --git a/l10n/de.json b/l10n/de.json index 51bf5ad..b013783 100644 --- a/l10n/de.json +++ b/l10n/de.json @@ -131,6 +131,7 @@ "All activity": "Alle Aktivitäten", "All changes are saved": "Alle Änderungen sind gespeichert", "All collection files are available": "Alle Dateien der Sammlung sind verfügbar", + "All feedback": "Gesamtes Feedback", "All galleries": "Alle Galerien", "All photos": "Alle Fotos", "All source files available": "Alle Quelldateien verfügbar", @@ -526,6 +527,7 @@ "Gallery updates for “%s”": "Aktualisierungen für „%s“", "Gallery view": "Galerieansicht", "General": "Allgemein", + "General comment": "Allgemeiner Kommentar", "Generated locally in your browser. The gallery link is not sent to a third party.": "Wird lokal in Ihrem Browser erstellt. Der Galerielink wird nicht an Dritte gesendet.", "Give a Nextcloud user or group view-only or editing access.": "Geben Sie einer Nextcloud-Person oder -Gruppe Lese- oder Bearbeitungszugriff.", "Give every audience its own URL, permissions and folder scope. No password or token is written to the audit log.": "Gib jeder Zielgruppe eine eigene URL, eigene Berechtigungen und einen eigenen Ordnerbereich. Passwörter und Token werden nie im Auditprotokoll gespeichert.", @@ -1060,6 +1062,7 @@ "Select all visible files": "Alle sichtbaren Dateien auswählen", "Select photos across pages, then download them together.": "Fotos seitenübergreifend auswählen und anschließend gemeinsam herunterladen.", "Select public image information to make this panel available to guests.": "Wählen Sie öffentliche Bildinformationen aus, damit dieses Panel Gästen zur Verfügung steht.", + "Selected annotation": "Ausgewählte Anmerkung", "Selected file actions": "Aktionen für ausgewählte Dateien", "Selected files": "Ausgewählte Dateien", "Selected information appears in the photo viewer. Changing this selection opens that preview scene.": "Ausgewählte Informationen erscheinen in der Fotoansicht. Änderungen an dieser Auswahl öffnen die entsprechende Vorschauszene.", diff --git a/l10n/en.js b/l10n/en.js index 33b9f6e..4fc7395 100644 --- a/l10n/en.js +++ b/l10n/en.js @@ -130,6 +130,7 @@ OC.L10N.register("proofing_gallery", { "All activity": "All activity", "All changes are saved": "All changes are saved", "All collection files are available": "All collection files are available", + "All feedback": "All feedback", "All galleries": "All galleries", "All photos": "All photos", "All source files available": "All source files available", @@ -525,6 +526,7 @@ OC.L10N.register("proofing_gallery", { "Gallery updates for “%s”": "Gallery updates for “%s”", "Gallery view": "Gallery view", "General": "General", + "General comment": "General comment", "Generated locally in your browser. The gallery link is not sent to a third party.": "Generated locally in your browser. The gallery link is not sent to a third party.", "Give a Nextcloud user or group view-only or editing access.": "Give a Nextcloud user or group view-only or editing access.", "Give every audience its own URL, permissions and folder scope. No password or token is written to the audit log.": "Give every audience its own URL, permissions and folder scope. No password or token is written to the audit log.", @@ -1059,6 +1061,7 @@ OC.L10N.register("proofing_gallery", { "Select all visible files": "Select all visible files", "Select photos across pages, then download them together.": "Select photos across pages, then download them together.", "Select public image information to make this panel available to guests.": "Select public image information to make this panel available to guests.", + "Selected annotation": "Selected annotation", "Selected file actions": "Selected file actions", "Selected files": "Selected files", "Selected information appears in the photo viewer. Changing this selection opens that preview scene.": "Selected information appears in the photo viewer. Changing this selection opens that preview scene.", diff --git a/l10n/en.json b/l10n/en.json index 694f6e2..8b487e1 100644 --- a/l10n/en.json +++ b/l10n/en.json @@ -131,6 +131,7 @@ "All activity": "All activity", "All changes are saved": "All changes are saved", "All collection files are available": "All collection files are available", + "All feedback": "All feedback", "All galleries": "All galleries", "All photos": "All photos", "All source files available": "All source files available", @@ -526,6 +527,7 @@ "Gallery updates for “%s”": "Gallery updates for “%s”", "Gallery view": "Gallery view", "General": "General", + "General comment": "General comment", "Generated locally in your browser. The gallery link is not sent to a third party.": "Generated locally in your browser. The gallery link is not sent to a third party.", "Give a Nextcloud user or group view-only or editing access.": "Give a Nextcloud user or group view-only or editing access.", "Give every audience its own URL, permissions and folder scope. No password or token is written to the audit log.": "Give every audience its own URL, permissions and folder scope. No password or token is written to the audit log.", @@ -1060,6 +1062,7 @@ "Select all visible files": "Select all visible files", "Select photos across pages, then download them together.": "Select photos across pages, then download them together.", "Select public image information to make this panel available to guests.": "Select public image information to make this panel available to guests.", + "Selected annotation": "Selected annotation", "Selected file actions": "Selected file actions", "Selected files": "Selected files", "Selected information appears in the photo viewer. Changing this selection opens that preview scene.": "Selected information appears in the photo viewer. Changing this selection opens that preview scene.", diff --git a/lib/Dto/Settings/ReviewSettings.php b/lib/Dto/Settings/ReviewSettings.php index f6c0b9a..08f2077 100644 --- a/lib/Dto/Settings/ReviewSettings.php +++ b/lib/Dto/Settings/ReviewSettings.php @@ -33,7 +33,7 @@ public static function defaults(): array { return [ 'visibility' => 'collaborative', 'likes' => true, 'colors' => true, 'comments' => true, 'annotations' => true, 'selections' => true, 'ratings' => false, 'pick' => false, - 'colorLabels' => ['Favorit', 'Auswahl', 'Überarbeiten', 'Ablehnen'], + 'colorLabels' => ['Favorite', 'Selected', 'Needs changes', 'Rejected'], 'colorEnabled' => [true, true, true, true], 'selectionWarningThreshold' => 0, ]; } diff --git a/src/components/PublicLightbox.vue b/src/components/PublicLightbox.vue index a7b357f..18d6a73 100644 --- a/src/components/PublicLightbox.vue +++ b/src/components/PublicLightbox.vue @@ -35,9 +35,10 @@ import StarOutlineIcon from 'vue-material-design-icons/StarOutline.vue' import { usePublicLightboxAnnotations } from '../composables/usePublicLightboxAnnotations.ts' import type { GallerySettings } from '../domain/gallerySettings.ts' -import { annotationNumbersByComment, shouldAutoHideLightboxChrome } from '../domain/lightboxReview.ts' +import { annotationNumbersByComment, findSelectedAnnotationComment, shouldAutoHideLightboxChrome } from '../domain/lightboxReview.ts' import type { CollaborationState, MediaItem } from '../publicTypes.ts' import PublicLightboxAnnotations from './PublicLightboxAnnotations.vue' +import PublicLightboxComments from './PublicLightboxComments.vue' import PublicLightboxFilmstrip from './PublicLightboxFilmstrip.vue' import PublicLightboxHeader from './PublicLightboxHeader.vue' import PublicLightboxMetadata from './PublicLightboxMetadata.vue' @@ -171,6 +172,11 @@ const { } = annotations const annotationNumbers = computed(() => annotationNumbersByComment(activeComments.value)) +const selectedAnnotationComment = computed(() => findSelectedAnnotationComment(activeComments.value, selectedCommentId.value)) +const visibleComments = computed(() => selectedAnnotationComment.value ? [selectedAnnotationComment.value] : activeComments.value) + +function showAllFeedback() { selectedCommentId.value = null } +function openFeedback() { showAllFeedback(); feedbackOpen.value = true; metadataOpen.value = false } function bindPhotoSwipeEvents() { if (!pswp) return @@ -510,7 +516,7 @@ async function toggleLike() { await props.mutate(`media/${item.id}/like`, 'POST') } -async function openFeedbackAndLike() { feedbackOpen.value = true; await toggleLike() } +async function openFeedbackAndLike() { openFeedback(); await toggleLike() } async function setColor(value: string) { const item = activeItem.value @@ -574,7 +580,7 @@ async function saveEditedComment(commentId: number) { @close="close" @zoom="zoom" @like="openFeedbackAndLike" - @feedback="feedbackOpen = true; metadataOpen = false" + @feedback="openFeedback" @info="metadataOpen = true; feedbackOpen = false" @more="actionMenuOpen = true" />