diff --git a/src/apps/dashboard/static/apps/dashboard/note.js b/src/apps/dashboard/static/apps/dashboard/note.js index d292eea..c3c806d 100644 --- a/src/apps/dashboard/static/apps/dashboard/note.js +++ b/src/apps/dashboard/static/apps/dashboard/note.js @@ -92,7 +92,17 @@ const Brief = (() => { banner.querySelector('.note-banner__fyi').addEventListener('click', function () { if (opts.target_selector) { var target = document.querySelector(opts.target_selector); - if (target) target.classList.add('bud-duplicate-flash'); + if (target) { + target.classList.add('bud-duplicate-flash'); + // Auto-ease-out — the SCSS rule has a 600ms transition + // on color + text-shadow, so removing the class 3s after + // FYI lets the peak state breathe before fading back to + // the default text styling. Without this the flash + // persisted til page refresh. + setTimeout(function () { + target.classList.remove('bud-duplicate-flash'); + }, 3000); + } } banner.remove(); }); diff --git a/src/static/tests/NoteSpec.js b/src/static/tests/NoteSpec.js index 35c8065..71d091a 100644 --- a/src/static/tests/NoteSpec.js +++ b/src/static/tests/NoteSpec.js @@ -230,6 +230,25 @@ describe('Brief.showDuplicateBanner', () => { expect(name.classList.contains('bud-duplicate-flash')).toBeTrue(); }); + it('D7b: .bud-duplicate-flash auto-eases out — class is removed ~3s after FYI', () => { + jasmine.clock().install(); + try { + Brief.showDuplicateBanner({ + display_name: 'alice', + target_selector: '.bud-entry[data-bud-id="42"] .bud-name', + }); + document.querySelector('.note-banner__fyi').click(); + const name = fixture.querySelector('.bud-name'); + // Immediately after FYI: flash is on (the visible peak state). + expect(name.classList.contains('bud-duplicate-flash')).toBeTrue(); + // After the auto-dismiss window: flash is gone. + jasmine.clock().tick(3001); + expect(name.classList.contains('bud-duplicate-flash')).toBeFalse(); + } finally { + jasmine.clock().uninstall(); + } + }); + it('D8: FYI dismisses cleanly when target_selector is missing', () => { Brief.showDuplicateBanner({ display_name: 'alice' }); document.querySelector('.note-banner__fyi').click(); diff --git a/src/static_src/scss/_bud.scss b/src/static_src/scss/_bud.scss index 80f4820..ab14acb 100644 --- a/src/static_src/scss/_bud.scss +++ b/src/static_src/scss/_bud.scss @@ -177,11 +177,12 @@ html:has(#id_kit_bag_dialog[open]) #id_bud_btn { // Eased-in flash applied by Brief.showDuplicateBanner's FYI button to a // caller-supplied target element — one of .bud-entry .bud-name (My Buds), // .post-recipient (post share), or .gate-slot.filled (gatekeeper invite). -// Persists until page refresh; --terUser color + --ninUser text-shadow -// per the duplicate-guard spec. +// Class is auto-removed by note.js 3s after FYI; SCSS transition handles +// both the ease-in (on add) AND the ease-out (on remove). Palette swap +// vs. the original spec: color = --ninUser, text-shadow = --terUser. .bud-duplicate-flash { - color: rgba(var(--terUser), 1); - text-shadow: 0 0 0.5em rgba(var(--ninUser), 1); + color: rgba(var(--ninUser), 1); + text-shadow: 0 0 0.5em rgba(var(--terUser), 1); transition: color 600ms ease, text-shadow 600ms ease; } diff --git a/src/static_src/tests/NoteSpec.js b/src/static_src/tests/NoteSpec.js index 35c8065..71d091a 100644 --- a/src/static_src/tests/NoteSpec.js +++ b/src/static_src/tests/NoteSpec.js @@ -230,6 +230,25 @@ describe('Brief.showDuplicateBanner', () => { expect(name.classList.contains('bud-duplicate-flash')).toBeTrue(); }); + it('D7b: .bud-duplicate-flash auto-eases out — class is removed ~3s after FYI', () => { + jasmine.clock().install(); + try { + Brief.showDuplicateBanner({ + display_name: 'alice', + target_selector: '.bud-entry[data-bud-id="42"] .bud-name', + }); + document.querySelector('.note-banner__fyi').click(); + const name = fixture.querySelector('.bud-name'); + // Immediately after FYI: flash is on (the visible peak state). + expect(name.classList.contains('bud-duplicate-flash')).toBeTrue(); + // After the auto-dismiss window: flash is gone. + jasmine.clock().tick(3001); + expect(name.classList.contains('bud-duplicate-flash')).toBeFalse(); + } finally { + jasmine.clock().uninstall(); + } + }); + it('D8: FYI dismisses cleanly when target_selector is missing', () => { Brief.showDuplicateBanner({ display_name: 'alice' }); document.querySelector('.note-banner__fyi').click();