Compare commits

...
3 Commits
Author SHA1 Message Date
raucao 2d3808a097 1.32.1
CI / Lint (push) Successful in 1m7s
CI / Test (push) Successful in 1m22s
2026-08-30 09:10:10 -06:00
raucao 6aa13d7243 Merge pull request 'Fix photo dropdown not closing after deletion' (#92) from bug/photo_menu into master
CI / Lint (push) Successful in 1m10s
CI / Test (push) Successful in 1m28s
Reviewed-on: #92
2026-08-30 15:05:12 +00:00
raucao 6804e80528 Fix photo dropdown not closing after deletion
CI / Lint (pull_request) Successful in 1m12s
CI / Test (pull_request) Successful in 1m27s
Release Drafter / Update release notes draft (pull_request) Successful in 5s
fixes #90
2026-08-30 09:02:07 -06:00
7 changed files with 85 additions and 17 deletions
+25 -11
View File
@@ -52,7 +52,7 @@ const GalleryContent = <template>
<button
class="dropdown-item text-danger"
type="button"
{{on "click" (fn @deletePhotoTask.perform closeMenu)}}
{{on "click" (fn @deletePhoto closeMenu)}}
>Delete Photo</button>
{{/if}}
</DropdownMenu>
@@ -259,21 +259,27 @@ export default class PhotoGallery extends Component {
this.zapModalOpen = false;
}
deletePhotoTask = task(async (closeMenu) => {
@action
deletePhoto(closeMenu, e) {
e?.stopPropagation();
if (closeMenu) closeMenu();
if (
!confirm(
'Are you sure you want to delete this photo? This cannot be undone.'
)
) {
if (closeMenu) closeMenu();
return;
}
this.deletePhotoTask.perform();
}
deletePhotoTask = task(async () => {
const deletedEventId = this.currentPhoto.eventId;
const deletedIndex = this.args.photos?.indexOf(this.currentPhoto) ?? -1;
try {
const eventId = this.currentPhoto.eventId;
// Publish Nostr kind: 5 deletion event first so we don't end up with dead blossom links on a failure
const tags = [['e', eventId]];
const tags = [['e', deletedEventId]];
if (this.currentPhoto.placeIdentifier) {
tags.push(['i', this.currentPhoto.placeIdentifier]);
@@ -316,12 +322,20 @@ export default class PhotoGallery extends Component {
this.toast.show('Photo deleted successfully');
if (closeMenu) closeMenu();
this.handleClose();
// Navigate to the next remaining photo, or close the gallery if none left.
// this.args.photos may be stale until Glimmer re-renders, so filter locally.
const remaining = (this.args.photos || []).filter(
(p) => p.eventId !== deletedEventId
);
if (remaining.length === 0) {
this.handleClose();
} else {
this.currentPhoto =
remaining[Math.min(deletedIndex, remaining.length - 1)];
}
} catch (e) {
console.error('Failed to delete photo:', e);
this.toast.show('Failed to delete photo: ' + e.message);
if (closeMenu) closeMenu();
}
});
@@ -333,7 +347,7 @@ export default class PhotoGallery extends Component {
@handleKeydown={{this.handleKeydown}}
@copyEventId={{this.copyEventId}}
@canDeletePhoto={{this.canDeletePhoto}}
@deletePhotoTask={{this.deletePhotoTask}}
@deletePhoto={{this.deletePhoto}}
@handleClose={{this.handleClose}}
@photos={{@photos}}
@currentPhoto={{this.currentPhoto}}
@@ -356,7 +370,7 @@ export default class PhotoGallery extends Component {
@handleKeydown={{this.handleKeydown}}
@copyEventId={{this.copyEventId}}
@canDeletePhoto={{this.canDeletePhoto}}
@deletePhotoTask={{this.deletePhotoTask}}
@deletePhoto={{this.deletePhoto}}
@handleClose={{this.handleClose}}
@photos={{@photos}}
@currentPhoto={{this.currentPhoto}}
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "marco",
"version": "1.32.0",
"version": "1.32.1",
"private": true,
"description": "Unhosted maps app",
"repository": {
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -42,7 +42,7 @@
<meta name="msapplication-TileColor" content="#F6E9A6">
<meta name="msapplication-TileImage" content="/icons/icon-144.png">
<script type="module" crossorigin src="/assets/main-Znuwpp9Y.js"></script>
<script type="module" crossorigin src="/assets/main-qw72UzgO.js"></script>
<link rel="stylesheet" crossorigin href="/assets/main-CEhcTO-R.css">
</head>
<body>
@@ -133,6 +133,9 @@ module('Integration | Component | photo-gallery', function (hooks) {
assert.ok(confirmStub.calledOnce, 'confirmation dialog was shown');
assert.ok(blossomSpy.notCalled, 'blossom.delete was NOT called');
assert
.dom('.dropdown-popover')
.doesNotExist('dropdown menu was closed on cancel');
});
test('it performs full deletion flow when confirmed', async function (assert) {
@@ -226,7 +229,58 @@ module('Integration | Component | photo-gallery', function (hooks) {
toastSpy.calledWith('Photo deleted successfully'),
'success toast was shown'
);
assert.ok(closed, 'gallery was closed after deletion');
assert
.dom('.dropdown-popover')
.doesNotExist('dropdown menu was closed after deletion');
assert.notOk(closed, 'gallery was kept open after deletion');
});
test('it closes the gallery when deleting the last remaining photo', async function (assert) {
this.nostrAuth.pubkey = USER_A;
this.settings.update('experimentalEnablePhotoDeletion', true);
Object.defineProperty(this.nostrAuth, 'signer', {
configurable: true,
get: () => ({
signEvent: async (e) => ({
...e,
id: 'a3b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1',
sig: 'b3b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1',
pubkey: USER_A,
}),
getPublicKey: async () => USER_A,
}),
});
this.set('photos', [this.photos[0]]);
this.selectedPhoto = this.photos[0];
let closed = false;
this.handleClose = () => {
closed = true;
};
sinon.stub(window, 'confirm').returns(true);
sinon.stub(this.blossom, 'delete').resolves();
sinon.stub(this.nostrRelay, 'publish').resolves();
sinon.stub(this.nostrData.store, 'add');
await render(
<template>
<div id="test-container">
<div id="modal-portal"></div>
<PhotoGallery
@photos={{this.photos}}
@selectedPhoto={{this.selectedPhoto}}
@onClose={{this.handleClose}}
/>
</div>
</template>
);
await click('.dropdown-trigger-btn');
await click('.dropdown-item.text-danger');
assert.ok(closed, 'gallery was closed when last photo was deleted');
});
test('it copies event id to clipboard', async function (assert) {