进行了一些小优化
This commit is contained in:
@@ -25,6 +25,7 @@
|
||||
!/tests/widget-system/auth-source-contract.test.cjs
|
||||
!/tests/widget-system/user-badges-contract.test.cjs
|
||||
!/tests/widget-system/user-features-contract.test.cjs
|
||||
!/tests/widget-system/notification-preferences-contract.test.cjs
|
||||
!/tests/widget-system/asset-ledger-contract.test.cjs
|
||||
!/tests/widget-system/financial-flow-contract.test.cjs
|
||||
!/tests/widget-system/pay-orders-filter-contract.test.cjs
|
||||
|
||||
@@ -2,6 +2,134 @@
|
||||
* User Center — account settings + invite panel
|
||||
*/
|
||||
|
||||
/* 消息通知偏好 */
|
||||
.uc-notification-preferences__header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 20px;
|
||||
padding-bottom: 20px;
|
||||
border-bottom: 1px solid var(--color-line-soft, rgba(0,0,0,0.06));
|
||||
}
|
||||
|
||||
.uc-notification-preferences__header h2 {
|
||||
margin: 0 0 6px;
|
||||
color: var(--color-ink);
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.uc-notification-preferences__header p {
|
||||
margin: 0;
|
||||
color: var(--color-ink-4);
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.uc-notification-preferences__header-icon {
|
||||
display: grid;
|
||||
place-items: center;
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
flex: 0 0 44px;
|
||||
border-radius: 14px;
|
||||
color: var(--color-primary);
|
||||
background: var(--color-primary-soft);
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.uc-notification-preferences__list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.uc-notification-preference {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 24px;
|
||||
min-height: 82px;
|
||||
padding: 18px 0;
|
||||
border-bottom: 1px solid var(--color-line-soft, rgba(0,0,0,0.06));
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.uc-notification-preference__copy {
|
||||
display: flex;
|
||||
min-width: 0;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.uc-notification-preference__copy strong {
|
||||
color: var(--color-ink);
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
.uc-notification-preference__copy small {
|
||||
color: var(--color-ink-4);
|
||||
font-size: 13px;
|
||||
font-weight: 400;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.uc-notification-switch {
|
||||
position: relative;
|
||||
display: inline-flex;
|
||||
width: 44px;
|
||||
height: 24px;
|
||||
flex: 0 0 44px;
|
||||
}
|
||||
|
||||
.uc-notification-switch input {
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.uc-notification-switch span {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 999px;
|
||||
background: var(--color-line, #cbd5e1);
|
||||
transition: background-color 0.2s ease;
|
||||
}
|
||||
|
||||
.uc-notification-switch span::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 3px;
|
||||
left: 3px;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
border-radius: 50%;
|
||||
background: #fff;
|
||||
box-shadow: 0 1px 3px rgba(15,23,42,0.22);
|
||||
transition: transform 0.2s ease;
|
||||
}
|
||||
|
||||
.uc-notification-switch input:checked + span {
|
||||
background: var(--color-primary);
|
||||
}
|
||||
|
||||
.uc-notification-switch input:checked + span::after {
|
||||
transform: translateX(20px);
|
||||
}
|
||||
|
||||
.uc-notification-switch input:focus-visible + span {
|
||||
outline: 2px solid var(--color-primary);
|
||||
outline-offset: 3px;
|
||||
}
|
||||
|
||||
.uc-notification-preferences__footer {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
padding-top: 20px;
|
||||
}
|
||||
|
||||
.uc-notification-preferences__footer .btn-primary {
|
||||
min-width: 112px;
|
||||
}
|
||||
|
||||
/* 设置区增强 */
|
||||
.uc-content .settings-section {
|
||||
margin-bottom: 24px;
|
||||
|
||||
@@ -577,6 +577,58 @@
|
||||
}
|
||||
}
|
||||
|
||||
/* ==================== 消息通知偏好 ==================== */
|
||||
function initNotificationPreferences(root) {
|
||||
root = root || document;
|
||||
var form = root.matches && root.matches('[data-aksrt-notification-preferences]')
|
||||
? root
|
||||
: root.querySelector('[data-aksrt-notification-preferences]');
|
||||
if (!form || form.dataset.aksrtInitialized === 'true') return;
|
||||
form.dataset.aksrtInitialized = 'true';
|
||||
|
||||
form.addEventListener('submit', function(e) {
|
||||
e.preventDefault();
|
||||
var button = form.querySelector('[type="submit"]');
|
||||
if (!button || button.disabled) return;
|
||||
var toastId = 'aksrt-notification-preferences-toast';
|
||||
var restUrl = (window.aksrtData && (window.aksrtData.restUrl || window.aksrtData.apiUrl)) || cfg.restUrl || '';
|
||||
var nonce = (window.aksrtData && (window.aksrtData.restNonce || window.aksrtData.nonce)) || cfg.restNonce || '';
|
||||
var originalText = button.textContent;
|
||||
var preferences = {};
|
||||
['posts', 'like', 'system'].forEach(function(key) {
|
||||
var input = form.querySelector('[name="' + key + '"]');
|
||||
preferences[key] = Boolean(input && input.checked);
|
||||
});
|
||||
|
||||
button.disabled = true;
|
||||
button.textContent = i18n.saving || '保存中…';
|
||||
notyf(i18n.saving || '正在保存设置…', 'load', '', toastId);
|
||||
fetch(restUrl.replace(/\/$/, '') + '/user/notification-preferences', {
|
||||
method: 'POST',
|
||||
credentials: 'same-origin',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-WP-Nonce': nonce
|
||||
},
|
||||
body: JSON.stringify({ preferences: preferences })
|
||||
}).then(function(response) {
|
||||
return response.json().then(function(payload) {
|
||||
if (!response.ok || !payload || !payload.success) {
|
||||
throw new Error(payload && payload.message ? payload.message : (i18n.saveFailed || '保存失败'));
|
||||
}
|
||||
return payload;
|
||||
});
|
||||
}).then(function(payload) {
|
||||
notyf(payload.message || i18n.saved || '设置已保存', 'success', 5, toastId);
|
||||
}).catch(function(error) {
|
||||
notyf(error.message || i18n.networkError || '网络错误,请重试', 'danger', 5, toastId);
|
||||
}).finally(function() {
|
||||
button.disabled = false;
|
||||
button.textContent = originalText;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/* ==================== 账号设置:弹窗 / 密码 / 邮箱 / 绑定 ==================== */
|
||||
function initSettings(root) {
|
||||
if (!ajaxUrl || !settingsNonce) return;
|
||||
@@ -873,6 +925,9 @@
|
||||
initSettings(root);
|
||||
initBindPhone(root);
|
||||
});
|
||||
window.AksrtFrontend.register('aksrt-notification-preferences', function(root) {
|
||||
initNotificationPreferences(root);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -884,6 +939,7 @@
|
||||
initUnfavorite();
|
||||
initPostDeletion();
|
||||
initNotifications();
|
||||
initNotificationPreferences();
|
||||
initSettings();
|
||||
initBindPhone();
|
||||
initSettingsCallbackToast();
|
||||
@@ -895,6 +951,7 @@
|
||||
initUnfavorite();
|
||||
initPostDeletion();
|
||||
initNotifications();
|
||||
initNotificationPreferences();
|
||||
initSettings();
|
||||
initBindPhone();
|
||||
initSettingsCallbackToast();
|
||||
|
||||
@@ -181,6 +181,9 @@ if (class_exists('\AKSRT\User\Restriction')) {
|
||||
if (class_exists('\AKSRT\User\Center')) {
|
||||
\AKSRT\User\Center::get_instance()->init();
|
||||
}
|
||||
if (class_exists('\AKSRT\User\NotificationPreferences')) {
|
||||
\AKSRT\User\NotificationPreferences::init();
|
||||
}
|
||||
if (class_exists('\AKSRT\User\BadgeAutoAward')) {
|
||||
\AKSRT\User\BadgeAutoAward::get_instance()->init();
|
||||
}
|
||||
|
||||
@@ -178,6 +178,7 @@ if (!function_exists('aksrt_capture_user_center_view')) {
|
||||
'orders' => 'orders',
|
||||
'auth' => 'auth',
|
||||
'revenue' => 'revenue',
|
||||
'msg' => 'msg',
|
||||
'settings' => 'settings',
|
||||
'header' => 'header',
|
||||
'sidebar' => 'sidebar',
|
||||
@@ -201,7 +202,7 @@ if (!function_exists('aksrt_capture_user_center_panel')) {
|
||||
* Capture a built-in or registered user-center panel body.
|
||||
*/
|
||||
function aksrt_capture_user_center_panel(string $tab_key, array $tab, array $context): string {
|
||||
$built_in_tabs = ['profile', 'level', 'creator', 'posts', 'follow', 'comments', 'favorites', 'credits', 'vip', 'orders', 'auth', 'revenue', 'settings'];
|
||||
$built_in_tabs = ['profile', 'level', 'creator', 'posts', 'follow', 'comments', 'favorites', 'credits', 'vip', 'orders', 'auth', 'revenue', 'msg', 'settings'];
|
||||
if (in_array($tab_key, $built_in_tabs, true)) {
|
||||
return aksrt_capture_user_center_view($tab_key, $context);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
/**
|
||||
* User center notification preferences panel.
|
||||
*
|
||||
* @package AKSRT
|
||||
*/
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
$context = is_array($args['context'] ?? null) ? $args['context'] : [];
|
||||
$user_id = (int) ($context['user_id'] ?? get_current_user_id());
|
||||
$preferences = class_exists('\AKSRT\User\NotificationPreferences')
|
||||
? \AKSRT\User\NotificationPreferences::get($user_id)
|
||||
: ['posts' => true, 'like' => true, 'system' => true];
|
||||
$items = [
|
||||
'posts' => [
|
||||
'title' => __('文章评论', 'aksrt'),
|
||||
'description' => __('接收文章、评论、收藏等相关消息', 'aksrt'),
|
||||
],
|
||||
'like' => [
|
||||
'title' => __('点赞喜欢', 'aksrt'),
|
||||
'description' => __('接收点赞、关注等相关消息', 'aksrt'),
|
||||
],
|
||||
'system' => [
|
||||
'title' => __('系统消息', 'aksrt'),
|
||||
'description' => __('接收订单、活动、等系统消息', 'aksrt'),
|
||||
],
|
||||
];
|
||||
?>
|
||||
<form class="uc-notification-preferences" data-aksrt-notification-preferences>
|
||||
<div class="uc-notification-preferences__header">
|
||||
<div>
|
||||
<h2><?php esc_html_e('消息通知', 'aksrt'); ?></h2>
|
||||
<p><?php esc_html_e('选择你希望接收的站内消息类型。', 'aksrt'); ?></p>
|
||||
</div>
|
||||
<span class="uc-notification-preferences__header-icon" aria-hidden="true"><i class="fa fas fa-bell"></i></span>
|
||||
</div>
|
||||
|
||||
<div class="uc-notification-preferences__list">
|
||||
<?php foreach ($items as $key => $item) : ?>
|
||||
<label class="uc-notification-preference" for="aksrt-notification-<?php echo esc_attr($key); ?>">
|
||||
<span class="uc-notification-preference__copy">
|
||||
<strong><?php echo esc_html($item['title']); ?></strong>
|
||||
<small><?php echo esc_html($item['description']); ?></small>
|
||||
</span>
|
||||
<span class="uc-notification-switch">
|
||||
<input id="aksrt-notification-<?php echo esc_attr($key); ?>" type="checkbox" name="<?php echo esc_attr($key); ?>" value="1" role="switch" <?php checked(!empty($preferences[$key])); ?>>
|
||||
<span aria-hidden="true"></span>
|
||||
</span>
|
||||
</label>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
|
||||
<div class="uc-notification-preferences__footer">
|
||||
<button type="submit" class="btn-primary"><?php esc_html_e('保存设置', 'aksrt'); ?></button>
|
||||
</div>
|
||||
</form>
|
||||
@@ -43,8 +43,8 @@ $settings = array_values(array_filter([
|
||||
'label' => __('消息通知', 'aksrt'),
|
||||
'icon' => 'bell',
|
||||
'tone' => 'notice',
|
||||
'url' => (string) ($messages_url ?? ''),
|
||||
'count' => (int) ($notification_unread_count ?? 0),
|
||||
'url' => isset($dashboard_tabs['msg']) ? aksrt_get_user_center_tab_url('msg') : '',
|
||||
'tab' => 'msg',
|
||||
],
|
||||
[
|
||||
'label' => __('个人资料', 'aksrt'),
|
||||
|
||||
@@ -32,7 +32,11 @@ function aksrt_get_user_notifications(int $user_id = 0): array {
|
||||
'status' => 'hold',
|
||||
]);
|
||||
|
||||
if ($pending_comments > 0 && (!class_exists('\AKSRT\Settings\Settings') || \AKSRT\Settings\Settings::notification_enabled('pending_comments'))) {
|
||||
if (
|
||||
$pending_comments > 0
|
||||
&& (!class_exists('\AKSRT\Settings\Settings') || \AKSRT\Settings\Settings::notification_enabled('pending_comments'))
|
||||
&& (!class_exists('\AKSRT\User\NotificationPreferences') || \AKSRT\User\NotificationPreferences::allows($user_id, 'pending_comments'))
|
||||
) {
|
||||
$items[] = [
|
||||
'type' => 'pending_comments',
|
||||
'title' => __('评论待审核', 'aksrt'),
|
||||
@@ -249,6 +253,9 @@ if (!function_exists('aksrt_add_user_notification')) :
|
||||
function aksrt_add_user_notification(int $user_id, array $payload): void {
|
||||
if ($user_id <= 0) return;
|
||||
$type = sanitize_key((string) ($payload['type'] ?? 'notice'));
|
||||
if (class_exists('\AKSRT\User\NotificationPreferences') && !\AKSRT\User\NotificationPreferences::allows($user_id, $type)) {
|
||||
return;
|
||||
}
|
||||
if (class_exists('\AKSRT\Settings\Settings') && !\AKSRT\Settings\Settings::notification_enabled($type)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -55,7 +55,7 @@ add_action('template_redirect', function () {
|
||||
$tab = $tab === 'balance' ? 'credits' : sanitize_key($tab);
|
||||
$allowed = class_exists('\\AKSRT\\User\\Center')
|
||||
? array_keys(\AKSRT\User\Center::get_instance()->get_dashboard_tabs())
|
||||
: ['profile', 'level', 'creator', 'posts', 'follow', 'comments', 'favorites', 'credits', 'vip', 'orders', 'auth', 'revenue', 'settings'];
|
||||
: ['profile', 'level', 'creator', 'posts', 'follow', 'comments', 'favorites', 'credits', 'vip', 'orders', 'auth', 'revenue', 'msg', 'settings'];
|
||||
if (!in_array($tab, $allowed, true)) {
|
||||
global $wp_query;
|
||||
if ($wp_query instanceof WP_Query) { $wp_query->set_404(); }
|
||||
|
||||
+5
-5
@@ -188,11 +188,11 @@ class Center {
|
||||
'priority' => 55,
|
||||
'type' => 'panel',
|
||||
],
|
||||
'notif' => [
|
||||
'msg' => [
|
||||
'label' => __('消息通知', 'aksrt'),
|
||||
'icon' => 'bell',
|
||||
'priority' => 60,
|
||||
'type' => 'link',
|
||||
'type' => 'panel',
|
||||
],
|
||||
'auth' => [
|
||||
'label' => __('官方认证', 'aksrt'),
|
||||
@@ -307,6 +307,9 @@ class Center {
|
||||
if (!in_array('settings', $order, true)) {
|
||||
$order[] = 'settings';
|
||||
}
|
||||
if (!in_array('msg', $order, true)) {
|
||||
$order[] = 'msg';
|
||||
}
|
||||
|
||||
foreach ($order as $key) {
|
||||
if (!isset($definitions[$key])) {
|
||||
@@ -339,9 +342,6 @@ class Center {
|
||||
$this->panels[$key] = $panel;
|
||||
}
|
||||
|
||||
// Notif is a link entry (messages page), not a dashboard panel body.
|
||||
$this->panels['notif'] = $definitions['notif'];
|
||||
|
||||
// Keep stable order by priority.
|
||||
uasort($this->panels, static function (array $a, array $b): int {
|
||||
return ((int) ($a['priority'] ?? 100)) <=> ((int) ($b['priority'] ?? 100));
|
||||
|
||||
@@ -0,0 +1,135 @@
|
||||
<?php
|
||||
/**
|
||||
* User notification category preferences and REST persistence.
|
||||
*
|
||||
* @package AKSRT
|
||||
*/
|
||||
|
||||
namespace AKSRT\User;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
final class NotificationPreferences {
|
||||
private const META_KEY = 'aksrt_notification_preferences';
|
||||
|
||||
private const CATEGORIES = ['posts', 'like', 'system'];
|
||||
|
||||
public static function init(): void {
|
||||
add_action('rest_api_init', [self::class, 'register_rest_route']);
|
||||
}
|
||||
|
||||
public static function register_rest_route(): void {
|
||||
register_rest_route('aksrt/v1', '/user/notification-preferences', [
|
||||
'methods' => \WP_REST_Server::CREATABLE,
|
||||
'callback' => [self::class, 'save'],
|
||||
'permission_callback' => static function (): bool {
|
||||
return get_current_user_id() > 0 && current_user_can('read');
|
||||
},
|
||||
'args' => [
|
||||
'preferences' => [
|
||||
'type' => 'object',
|
||||
'required' => true,
|
||||
'properties' => [
|
||||
'posts' => ['type' => 'boolean', 'required' => true],
|
||||
'like' => ['type' => 'boolean', 'required' => true],
|
||||
'system' => ['type' => 'boolean', 'required' => true],
|
||||
],
|
||||
'additionalProperties' => false,
|
||||
'validate_callback' => [self::class, 'validate_preferences'],
|
||||
'sanitize_callback' => [self::class, 'sanitize_preferences'],
|
||||
],
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
/** @return array{posts:bool,like:bool,system:bool} */
|
||||
public static function get(int $user_id): array {
|
||||
$defaults = array_fill_keys(self::CATEGORIES, true);
|
||||
$stored = get_user_meta($user_id, self::META_KEY, true);
|
||||
if (!is_array($stored)) {
|
||||
return $defaults;
|
||||
}
|
||||
|
||||
foreach (self::CATEGORIES as $category) {
|
||||
if (array_key_exists($category, $stored)) {
|
||||
$defaults[$category] = (bool) $stored[$category];
|
||||
}
|
||||
}
|
||||
return $defaults;
|
||||
}
|
||||
|
||||
public static function allows(int $user_id, string $notification_type): bool {
|
||||
if ($user_id <= 0) {
|
||||
return false;
|
||||
}
|
||||
$preferences = self::get($user_id);
|
||||
return $preferences[self::category_for_type($notification_type)];
|
||||
}
|
||||
|
||||
public static function category_for_type(string $notification_type): string {
|
||||
$type = sanitize_key($notification_type);
|
||||
if (
|
||||
in_array($type, ['posts', 'post', 'comment', 'favorite', 'pending_comments'], true)
|
||||
|| str_starts_with($type, 'post_')
|
||||
|| str_starts_with($type, 'comment_')
|
||||
|| str_starts_with($type, 'favorite_')
|
||||
) {
|
||||
return 'posts';
|
||||
}
|
||||
if (
|
||||
in_array($type, ['like', 'follow', 'followed', 'hot'], true)
|
||||
|| str_starts_with($type, 'like_')
|
||||
|| str_starts_with($type, 'follow_')
|
||||
|| str_starts_with($type, 'followed_')
|
||||
) {
|
||||
return 'like';
|
||||
}
|
||||
return 'system';
|
||||
}
|
||||
|
||||
public static function validate_preferences($value): bool {
|
||||
if (
|
||||
!is_array($value)
|
||||
|| count($value) !== count(self::CATEGORIES)
|
||||
|| array_diff(array_keys($value), self::CATEGORIES)
|
||||
|| array_diff(self::CATEGORIES, array_keys($value))
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
foreach (self::CATEGORIES as $category) {
|
||||
if (!is_bool($value[$category])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/** @return array{posts:bool,like:bool,system:bool} */
|
||||
public static function sanitize_preferences($value): array {
|
||||
$value = is_array($value) ? $value : [];
|
||||
return [
|
||||
'posts' => (bool) ($value['posts'] ?? false),
|
||||
'like' => (bool) ($value['like'] ?? false),
|
||||
'system' => (bool) ($value['system'] ?? false),
|
||||
];
|
||||
}
|
||||
|
||||
public static function save(\WP_REST_Request $request) {
|
||||
$user_id = get_current_user_id();
|
||||
if ($user_id <= 0) {
|
||||
return new \WP_Error('aksrt_notification_preferences_auth', __('请先登录', 'aksrt'), ['status' => 401]);
|
||||
}
|
||||
|
||||
$preferences = self::sanitize_preferences($request->get_param('preferences'));
|
||||
$result = update_user_meta($user_id, self::META_KEY, $preferences);
|
||||
if ($result === false && self::get($user_id) !== $preferences) {
|
||||
return new \WP_Error('aksrt_notification_preferences_save', __('消息通知设置保存失败', 'aksrt'), ['status' => 500]);
|
||||
}
|
||||
|
||||
return rest_ensure_response([
|
||||
'success' => true,
|
||||
'message' => __('消息通知设置已保存', 'aksrt'),
|
||||
'preferences' => $preferences,
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
const assert = require('node:assert/strict');
|
||||
const fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
const test = require('node:test');
|
||||
|
||||
const root = path.resolve(__dirname, '..', '..');
|
||||
const read = (file) => fs.readFileSync(path.join(root, file), 'utf8');
|
||||
|
||||
test('message notification settings are a canonical user/msg inline panel', () => {
|
||||
const center = read('src/User/Center.php');
|
||||
const helpers = read('inc/helpers/aksrt-user-center.php');
|
||||
const sidebar = read('inc/helpers/user-center-renderers/sidebar-nav.php');
|
||||
const routing = read('inc/modules/user-parts/routing.php');
|
||||
|
||||
assert.match(center, /'msg'\s*=>\s*\[[\s\S]*?'type'\s*=>\s*'panel'/);
|
||||
assert.doesNotMatch(center, /'notif'\s*=>/);
|
||||
assert.match(helpers, /'msg'\s*=>\s*'msg'/);
|
||||
assert.match(sidebar, /aksrt_get_user_center_tab_url\('msg'\)/);
|
||||
assert.match(sidebar, /'tab'\s*=>\s*'msg'/);
|
||||
assert.doesNotMatch(sidebar, /'url'\s*=>\s*\(string\) \(\$messages_url/);
|
||||
assert.match(routing, /'revenue', 'msg', 'settings'/);
|
||||
});
|
||||
|
||||
test('notification preference endpoint stores only the three boolean categories', () => {
|
||||
const controller = read('src/User/NotificationPreferences.php');
|
||||
|
||||
assert.match(controller, /META_KEY = 'aksrt_notification_preferences'/);
|
||||
assert.match(controller, /CATEGORIES = \['posts', 'like', 'system'\]/);
|
||||
assert.match(controller, /register_rest_route\('aksrt\/v1', '\/user\/notification-preferences'/);
|
||||
assert.match(controller, /permission_callback/);
|
||||
assert.match(controller, /current_user_can\('read'\)/);
|
||||
assert.match(controller, /'additionalProperties'\s*=>\s*false/);
|
||||
assert.match(controller, /array_diff\(array_keys\(\$value\), self::CATEGORIES\)/);
|
||||
assert.doesNotMatch(controller, /message_shield|migration|legacy/i);
|
||||
});
|
||||
|
||||
test('notification preference UI uses the global toast lifecycle', () => {
|
||||
const template = read('inc/helpers/user-center-renderers/profile-msg.php');
|
||||
const script = read('assets/js/routes/user-center.js');
|
||||
|
||||
for (const text of [
|
||||
'文章评论',
|
||||
'接收文章、评论、收藏等相关消息',
|
||||
'点赞喜欢',
|
||||
'接收点赞、关注等相关消息',
|
||||
'系统消息',
|
||||
'接收订单、活动、等系统消息',
|
||||
]) {
|
||||
assert.ok(template.includes(text));
|
||||
}
|
||||
assert.match(template, /role="switch"/);
|
||||
assert.match(script, /aksrt-notification-preferences-toast/);
|
||||
assert.match(script, /notyf\([^;]+?'load'[^;]+?toastId/);
|
||||
assert.match(script, /notyf\([^;]+?'success'[^;]+?toastId/);
|
||||
assert.match(script, /notyf\([^;]+?'danger'[^;]+?toastId/);
|
||||
assert.doesNotMatch(script, /\b(?:window\.)?alert\s*\(/);
|
||||
});
|
||||
|
||||
test('notification writes and stateful alerts honor the category preference', () => {
|
||||
const notifications = read('inc/modules/user-notifications.php');
|
||||
|
||||
assert.match(notifications, /NotificationPreferences::allows\(\$user_id, 'pending_comments'\)/);
|
||||
assert.match(notifications, /NotificationPreferences::allows\(\$user_id, \$type\)/);
|
||||
});
|
||||
Reference in New Issue
Block a user