From 35920fea07f6c7706c0b70d0547c8aa60d067546 Mon Sep 17 00:00:00 2001 From: David Keane Date: Mon, 25 May 2026 07:57:26 +0100 Subject: [PATCH] feat: persist empty-state notice dismissal via user_meta MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The "No active/completed notes found" notice was already dismissible per-page-load (WP core's X button hides it), but pressing X only cleared it for the current view — it returned on the next refresh. The dismissal is now persisted to user_meta per-user-per-list-type, so once closed it stays closed across page loads until the flag is reset. MECHANICS - inc/wp-notes-display.php now checks get_user_meta(uid, 'wp_notes_dismissed_empty_') before rendering and skips the notice render entirely when set. - New AJAX handler wp_ajax_wp_notes_dismiss_empty validates a nonce and the edit_posts capability, then writes the flag via update_user_meta(). type=active|completed; anything else 400s. - An inline jQuery handler in wp_notes_add_inline_scripts() listens for clicks on .wp-notes-empty .notice-dismiss (WP core's auto- injected X button) and fires the AJAX call. The visual hide is still WP core's job. - The notice element carries data-wp-notes-empty-type and a fresh per-render data-wp-notes-nonce for the round trip. RESET The flag is per-user-meta keyed wp_notes_dismissed_empty_active / wp_notes_dismissed_empty_completed. wp_delete_user removes them automatically. A UI button to reset dismissed notices is not built yet — flagged in the changelog as a future enhancement. Co-Authored-By: Claude Sonnet 4.6 --- CHANGELOG.md | 29 +++++++++++++++++++++++++++++ inc/wp-notes-display.php | 26 ++++++++++++++++++-------- wp-notes.php | 37 +++++++++++++++++++++++++++++++++++++ 3 files changed, 84 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2d3ed5d..68d2142 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,35 @@ Format: [Keep a Changelog 1.1.0](https://keepachangelog.com/en/1.1.0/) — versi ## [Unreleased] +### Added — Persistent dismissal of the empty-state notice (user_meta) +The "No active/completed notes found" notice was already +dismissible per-page-load, but pressing X only hid it for the +current view — it returned on the next refresh. The dismissal is +now persisted to **user_meta** per-user-per-list-type, so once you +close it, it stays closed until you reset the flag. + +**Mechanics:** +- `inc/wp-notes-display.php` checks + `get_user_meta(uid, 'wp_notes_dismissed_empty_')` before + rendering and skips the notice entirely when set. +- `wp_ajax_wp_notes_dismiss_empty` (new handler in `wp-notes.php`) + validates a nonce + `edit_posts` capability, then writes the flag + via `update_user_meta()`. Accepts `type` of `active` or + `completed`; rejects anything else. +- An inline jQuery handler in `wp_notes_add_inline_scripts()` listens + for clicks on `.wp-notes-empty .notice-dismiss` (WP core's auto- + injected X button), reads the data-attributes off the notice, and + fires the AJAX call. WP core still handles the visual hide. +- The notice element carries `data-wp-notes-empty-type` and a fresh + per-render `data-wp-notes-nonce` for the round trip. + +**Reset:** the flag is per-user-meta keyed +`wp_notes_dismissed_empty_active` / +`wp_notes_dismissed_empty_completed`. To make the notice reappear +for a user, an admin can clear those keys (or `wp_delete_user` +removes them automatically). A UI button to reset dismissed notices +is not built yet — flagged as a future enhancement if needed. + ### Fixed — Duplicate "Create a New WP Note" form at the bottom of My Notes A second, bare-bones *"Create a New WP Note"* form was being rendered at the **bottom** of the My Notes page, below the active diff --git a/inc/wp-notes-display.php b/inc/wp-notes-display.php index 06ea23f..51b88c6 100644 --- a/inc/wp-notes-display.php +++ b/inc/wp-notes-display.php @@ -22,14 +22,24 @@ function wp_notes_display_notes($type = 'active') { $section_title = ($type === 'active') ? 'Active Notes' : 'Completed Notes'; if (empty($notes)) { - /* is-dismissible adds an X button via WP core's common.js so - users can clear the empty-state message for the current view. - Dismissal is per-page-load (the message reappears on next - refresh if the list is still empty) — persistence across - reloads would need user_meta tracking and isn't built yet. */ - echo '
'; - echo '

' . esc_html__('No ' . ($type === 'active' ? 'active' : 'completed') . ' notes found.', 'a-wp-notes') . '

'; - echo '
'; + /* Per-user persistent dismissal: if the current user has + dismissed the empty-state notice for this list type before, + we skip rendering it entirely. The flag stays set until they + explicitly reset it (or until we extend this to auto-clear + when a note is created — Option B for a future iteration). + Flag key: wp_notes_dismissed_empty_active / _completed. */ + $dismiss_meta_key = 'wp_notes_dismissed_empty_' . $type; + if (get_user_meta(get_current_user_id(), $dismiss_meta_key, true)) { + return; + } + + $nonce = wp_create_nonce('wp_notes_dismiss_empty'); + printf( + '

%3$s

', + esc_attr($type), + esc_attr($nonce), + esc_html__('No ' . ($type === 'active' ? 'active' : 'completed') . ' notes found.', 'a-wp-notes') + ); return; } diff --git a/wp-notes.php b/wp-notes.php index 6a1c222..68a3736 100644 --- a/wp-notes.php +++ b/wp-notes.php @@ -656,10 +656,47 @@ function wp_notes_add_inline_scripts() { } }); }); + + // Persist the dismissal of the empty-state notice. + // WP core's common.js attaches the X button and hides the + // notice on click; we just hook the same click and fire an + // AJAX call to set the user_meta flag so the notice does + // not reappear on the next page load. + $(document).on('click', '.wp-notes-empty .notice-dismiss', function() { + var \$notice = $(this).closest('.wp-notes-empty'); + var type = \$notice.data('wp-notes-empty-type'); + var nonce = \$notice.data('wp-notes-nonce'); + if (!type || !nonce) { return; } + $.post(ajaxurl, { + action: 'wp_notes_dismiss_empty', + type: type, + nonce: nonce + }); + }); }); "; } +// Persist a user's dismissal of the "No active/completed notes found" +// empty-state notice so it doesn't reappear on the next page load. +// Triggered by inline JS in wp_notes_add_inline_scripts() when the +// user clicks the X on a .wp-notes-empty notice. +add_action('wp_ajax_wp_notes_dismiss_empty', 'wp_notes_ajax_dismiss_empty'); +function wp_notes_ajax_dismiss_empty() { + if (!current_user_can('edit_posts')) { + wp_send_json_error('Insufficient permissions.', 403); + } + check_ajax_referer('wp_notes_dismiss_empty', 'nonce'); + + $type = isset($_POST['type']) ? sanitize_key(wp_unslash($_POST['type'])) : ''; + if (!in_array($type, ['active', 'completed'], true)) { + wp_send_json_error('Invalid list type.', 400); + } + + update_user_meta(get_current_user_id(), 'wp_notes_dismissed_empty_' . $type, 1); + wp_send_json_success(['dismissed' => $type]); +} + // Handle saving edited notes add_action('wp_ajax_wp_notes_save_edit', 'wp_notes_save_edit'); function wp_notes_save_edit() {