diff --git a/app/components/app-menu/settings.gjs b/app/components/app-menu/settings.gjs index 67e32c7..2271394 100644 --- a/app/components/app-menu/settings.gjs +++ b/app/components/app-menu/settings.gjs @@ -6,6 +6,7 @@ import Icon from '#components/icon'; import AppMenuSettingsMapUi from './settings/map-ui'; import AppMenuSettingsApis from './settings/apis'; import AppMenuSettingsNostr from './settings/nostr'; +import AppMenuSettingsExperimental from './settings/experimental'; export default class AppMenuSettings extends Component { @service settings; @@ -35,6 +36,7 @@ export default class AppMenuSettings extends Component { + diff --git a/app/components/app-menu/settings/experimental.gjs b/app/components/app-menu/settings/experimental.gjs new file mode 100644 index 0000000..728fbe7 --- /dev/null +++ b/app/components/app-menu/settings/experimental.gjs @@ -0,0 +1,49 @@ +import Component from '@glimmer/component'; +import { on } from '@ember/modifier'; +import { service } from '@ember/service'; +import { fn } from '@ember/helper'; +import Icon from '#components/icon'; + +export default class AppMenuSettingsExperimental extends Component { + @service settings; + + + {{! template-lint-disable no-nested-interactive }} + + + + Experimental + + + + Enable photo deletion + (own photos) + + + On + + + Off + + + + + + +} diff --git a/app/components/photo-carousel.gjs b/app/components/photo-carousel.gjs index a9850d0..d901dfb 100644 --- a/app/components/photo-carousel.gjs +++ b/app/components/photo-carousel.gjs @@ -95,7 +95,7 @@ export default class PhotoCarousel extends Component { }, 500); } } else { - this.isProgrammaticScroll = false; + this.isProgrammaticScroll = false; } }; diff --git a/app/components/photo-gallery.gjs b/app/components/photo-gallery.gjs index 9bcd07d..34e6e27 100644 --- a/app/components/photo-gallery.gjs +++ b/app/components/photo-gallery.gjs @@ -34,7 +34,7 @@ const GalleryContent = type="button" {{on "click" (fn @copyEventId closeMenu)}} >Copy Photo Event ID - {{#if @isCreator}} + {{#if @canDeletePhoto}} { document.addEventListener('keydown', handler); return () => document.removeEventListener('keydown', handler); @@ -254,7 +261,7 @@ export default class PhotoGallery extends Component { @bindKeyboard={{this.bindKeyboard}} @handleKeydown={{this.handleKeydown}} @copyEventId={{this.copyEventId}} - @isCreator={{this.isCreator}} + @canDeletePhoto={{this.canDeletePhoto}} @deletePhotoTask={{this.deletePhotoTask}} @handleClose={{this.handleClose}} @photos={{@photos}} @@ -270,7 +277,7 @@ export default class PhotoGallery extends Component { @bindKeyboard={{this.bindKeyboard}} @handleKeydown={{this.handleKeydown}} @copyEventId={{this.copyEventId}} - @isCreator={{this.isCreator}} + @canDeletePhoto={{this.canDeletePhoto}} @deletePhotoTask={{this.deletePhotoTask}} @handleClose={{this.handleClose}} @photos={{@photos}} diff --git a/app/services/settings.js b/app/services/settings.js index 669a4ed..a198166 100644 --- a/app/services/settings.js +++ b/app/services/settings.js @@ -9,6 +9,7 @@ const DEFAULT_SETTINGS = { nostrPhotoFallbackUploads: false, nostrReadRelays: null, nostrWriteRelays: null, + experimentalEnablePhotoDeletion: false, }; export default class SettingsService extends Service { @@ -20,6 +21,8 @@ export default class SettingsService extends Service { DEFAULT_SETTINGS.nostrPhotoFallbackUploads; @tracked nostrReadRelays = DEFAULT_SETTINGS.nostrReadRelays; @tracked nostrWriteRelays = DEFAULT_SETTINGS.nostrWriteRelays; + @tracked experimentalEnablePhotoDeletion = + DEFAULT_SETTINGS.experimentalEnablePhotoDeletion; overpassApis = [ { @@ -108,6 +111,8 @@ export default class SettingsService extends Service { this.nostrPhotoFallbackUploads = finalSettings.nostrPhotoFallbackUploads; this.nostrReadRelays = finalSettings.nostrReadRelays; this.nostrWriteRelays = finalSettings.nostrWriteRelays; + this.experimentalEnablePhotoDeletion = + finalSettings.experimentalEnablePhotoDeletion; // Save to ensure migrated settings are stored in the new format this.saveSettings(); @@ -122,6 +127,7 @@ export default class SettingsService extends Service { nostrPhotoFallbackUploads: this.nostrPhotoFallbackUploads, nostrReadRelays: this.nostrReadRelays, nostrWriteRelays: this.nostrWriteRelays, + experimentalEnablePhotoDeletion: this.experimentalEnablePhotoDeletion, }; localStorage.setItem('marco:settings', JSON.stringify(settings)); } diff --git a/app/utils/icons.js b/app/utils/icons.js index 9ffb642..0bf5cb2 100644 --- a/app/utils/icons.js +++ b/app/utils/icons.js @@ -6,6 +6,7 @@ import featherCamera from 'feather-icons/dist/icons/camera.svg?raw'; import checkSquare from 'feather-icons/dist/icons/check-square.svg?raw'; import chevronLeft from 'feather-icons/dist/icons/chevron-left.svg?raw'; import chevronRight from 'feather-icons/dist/icons/chevron-right.svg?raw'; +import alertTriangle from 'feather-icons/dist/icons/alert-triangle.svg?raw'; import clock from 'feather-icons/dist/icons/clock.svg?raw'; import database from 'feather-icons/dist/icons/database.svg?raw'; import edit from 'feather-icons/dist/icons/edit.svg?raw'; @@ -146,6 +147,7 @@ const ICONS = { climbing_wall: climbingWall, check, 'alert-circle': alertCircle, + 'alert-triangle': alertTriangle, 'classical-building': classicalBuilding, 'classical-building-with-dome-and-flag': classicalBuildingWithDomeAndFlag, 'classical-building-with-flag': classicalBuildingWithFlag, diff --git a/tests/integration/components/photo-gallery-test.gjs b/tests/integration/components/photo-gallery-test.gjs index fe1c151..1d540e9 100644 --- a/tests/integration/components/photo-gallery-test.gjs +++ b/tests/integration/components/photo-gallery-test.gjs @@ -29,6 +29,7 @@ module('Integration | Component | photo-gallery', function (hooks) { this.nostrData = this.owner.lookup('service:nostrData'); this.nostrRelay = this.owner.lookup('service:nostrRelay'); this.toast = this.owner.lookup('service:toast'); + this.settings = this.owner.lookup('service:settings'); this.photos = [ { @@ -50,6 +51,7 @@ module('Integration | Component | photo-gallery', function (hooks) { hooks.afterEach(function () { sinon.restore(); + localStorage.removeItem('marco:settings'); }); test('it does not show delete button if user is not creator', async function (assert) { @@ -77,8 +79,9 @@ module('Integration | Component | photo-gallery', function (hooks) { .doesNotExist('Delete button is hidden for non-creator'); }); - test('it shows delete button if user is creator', async function (assert) { + test('it shows delete button if user is creator and setting is enabled', async function (assert) { this.nostrAuth.pubkey = 'userA'; // Matches photo1's pubkey + this.settings.update('experimentalEnablePhotoDeletion', true); // Enable the setting this.selectedPhoto = this.photos[0]; await render( @@ -99,12 +102,12 @@ module('Integration | Component | photo-gallery', function (hooks) { assert.dom('.dropdown-popover').exists('Dropdown opened'); assert .dom('.dropdown-item.text-danger') - .exists('Delete button is visible for creator'); - assert.dom('.dropdown-item.text-danger').hasText('Delete Photo'); + .exists('Delete button is visible for creator when setting is enabled'); }); test('it handles cancellation of deletion', async function (assert) { this.nostrAuth.pubkey = 'userA'; + this.settings.update('experimentalEnablePhotoDeletion', true); this.selectedPhoto = this.photos[0]; const confirmStub = sinon.stub(window, 'confirm').returns(false); @@ -131,6 +134,7 @@ module('Integration | Component | photo-gallery', function (hooks) { test('it performs full deletion flow when confirmed', async function (assert) { this.nostrAuth.pubkey = 'userA'; + this.settings.update('experimentalEnablePhotoDeletion', true); // Override the mock's getter just for this test Object.defineProperty(this.nostrAuth, 'signer', { configurable: true,