feat: persist empty-state notice dismissal via user_meta
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_<type>') 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 <noreply@anthropic.com>
This commit is contained in:
@@ -7,6 +7,35 @@ Format: [Keep a Changelog 1.1.0](https://keepachangelog.com/en/1.1.0/) — versi
|
|||||||
|
|
||||||
## [Unreleased]
|
## [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_<type>')` 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
|
### 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
|
A second, bare-bones *"Create a New WP Note"* form was being
|
||||||
rendered at the **bottom** of the My Notes page, below the active
|
rendered at the **bottom** of the My Notes page, below the active
|
||||||
|
|||||||
@@ -22,14 +22,24 @@ function wp_notes_display_notes($type = 'active') {
|
|||||||
$section_title = ($type === 'active') ? 'Active Notes' : 'Completed Notes';
|
$section_title = ($type === 'active') ? 'Active Notes' : 'Completed Notes';
|
||||||
|
|
||||||
if (empty($notes)) {
|
if (empty($notes)) {
|
||||||
/* is-dismissible adds an X button via WP core's common.js so
|
/* Per-user persistent dismissal: if the current user has
|
||||||
users can clear the empty-state message for the current view.
|
dismissed the empty-state notice for this list type before,
|
||||||
Dismissal is per-page-load (the message reappears on next
|
we skip rendering it entirely. The flag stays set until they
|
||||||
refresh if the list is still empty) — persistence across
|
explicitly reset it (or until we extend this to auto-clear
|
||||||
reloads would need user_meta tracking and isn't built yet. */
|
when a note is created — Option B for a future iteration).
|
||||||
echo '<div class="wp-notes-empty notice notice-info is-dismissible">';
|
Flag key: wp_notes_dismissed_empty_active / _completed. */
|
||||||
echo '<p>' . esc_html__('No ' . ($type === 'active' ? 'active' : 'completed') . ' notes found.', 'a-wp-notes') . '</p>';
|
$dismiss_meta_key = 'wp_notes_dismissed_empty_' . $type;
|
||||||
echo '</div>';
|
if (get_user_meta(get_current_user_id(), $dismiss_meta_key, true)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$nonce = wp_create_nonce('wp_notes_dismiss_empty');
|
||||||
|
printf(
|
||||||
|
'<div class="wp-notes-empty notice notice-info is-dismissible" data-wp-notes-empty-type="%1$s" data-wp-notes-nonce="%2$s"><p>%3$s</p></div>',
|
||||||
|
esc_attr($type),
|
||||||
|
esc_attr($nonce),
|
||||||
|
esc_html__('No ' . ($type === 'active' ? 'active' : 'completed') . ' notes found.', 'a-wp-notes')
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
</script>";
|
</script>";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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
|
// Handle saving edited notes
|
||||||
add_action('wp_ajax_wp_notes_save_edit', 'wp_notes_save_edit');
|
add_action('wp_ajax_wp_notes_save_edit', 'wp_notes_save_edit');
|
||||||
function wp_notes_save_edit() {
|
function wp_notes_save_edit() {
|
||||||
|
|||||||
Reference in New Issue
Block a user