feat(checkin): open reference-parity details modal from signed buttons
- Rebuild checkin details modal HTML 1:1 with reference theme: colorful header c-blue, user card (points/level), daily reward card and 7-day continuous mini cards - Signed entry buttons (header dropdown, user-card badge, user center) become clickable checkin-details-link modal triggers instead of disabled buttons - Swap data-signin-btn to details link in place after successful signin (reference .initiate-checkin replacement behavior) - Add aksrt-checkin-details-modal runtime shell (modal-mini 400px) - Remove dead code: legacy lite modal, admin-ajax details handler, /signin/history route, signin calendar; rebuild public bundles - Update contract tests to the new checkin modal contract
This commit is contained in:
1 parent
c289016324
commit
4653128a18
15 files changed
+211
-266
No files matched your search
@@ -461,6 +461,8 @@ trait ScriptsPart1c {
|
||||
'nonce' => wp_create_nonce('aksrt_public_nonce'),
|
||||
'restNonce' => wp_create_nonce('wp_rest'),
|
||||
'captchaSignin' => function_exists('aksrt_is_captcha_enabled_for') && aksrt_is_captcha_enabled_for('signin'),
|
||||
// 已签到详情弹窗(签到成功后就地替换入口按钮用)。
|
||||
'signinDetailsUrl' => esc_url_raw(rest_url('aksrt/v1/signin/details')),
|
||||
'visibilityNonce' => wp_create_nonce('aksrt_visibility'),
|
||||
'siteUrl' => home_url('/'),
|
||||
'themeUrl' => AKSRT_URI,
|
||||
|
||||
+78
-123
@@ -20,7 +20,6 @@ class Signin {
|
||||
|
||||
public function init(): void {
|
||||
add_action('rest_api_init', [$this, 'register_routes']);
|
||||
add_action('wp_ajax_aksrt_checkin_details_modal', [$this, 'ajax_details_modal']);
|
||||
}
|
||||
|
||||
public function register_routes(): void {
|
||||
@@ -34,11 +33,6 @@ class Signin {
|
||||
'callback' => [$this, 'rest_signin'],
|
||||
'permission_callback' => [$this, 'check_rest_permission'],
|
||||
]);
|
||||
register_rest_route('aksrt/v1', '/signin/history', [
|
||||
'methods' => 'GET',
|
||||
'callback' => [$this, 'rest_history'],
|
||||
'permission_callback' => [$this, 'check_rest_permission'],
|
||||
]);
|
||||
register_rest_route('aksrt/v1', '/signin/details', [
|
||||
'methods' => 'GET',
|
||||
'callback' => [$this, 'rest_details_html'],
|
||||
@@ -237,42 +231,12 @@ class Signin {
|
||||
do_action('aksrt_checkin_done', $user_id);
|
||||
}
|
||||
|
||||
/** GET /signin/history — last N days for calendar/modals. */
|
||||
public function rest_history(\WP_REST_Request $request) {
|
||||
$user_id = get_current_user_id();
|
||||
$days = max(7, min(90, (int) ($request->get_param('days') ?: 30)));
|
||||
$rows = $this->get_recent_signins($user_id, $days);
|
||||
$dates = [];
|
||||
foreach ($rows as $row) {
|
||||
if (!empty($row['signin_date'])) {
|
||||
$dates[] = (string) $row['signin_date'];
|
||||
}
|
||||
}
|
||||
$today = $this->today_date();
|
||||
return new \WP_REST_Response([
|
||||
'dates' => $dates,
|
||||
'streak' => $this->compute_current_streak($user_id, $today),
|
||||
'total' => $this->get_total_days($user_id),
|
||||
'signed' => (bool) $this->get_record($user_id, $today),
|
||||
'all_day' => (int) get_user_meta($user_id, 'aksrt_checkin_all_day', true) ?: $this->get_total_days($user_id),
|
||||
], 200);
|
||||
}
|
||||
|
||||
/** GET /signin/details — HTML fragment for remote modal. */
|
||||
public function rest_details_html(\WP_REST_Request $request) {
|
||||
$html = $this->render_details_modal_html(get_current_user_id());
|
||||
return new \WP_REST_Response(['success' => true, 'html' => $html], 200);
|
||||
}
|
||||
|
||||
/** admin-ajax HTML (remote modal fetch often hits admin-ajax). */
|
||||
public function ajax_details_modal(): void {
|
||||
if (!is_user_logged_in()) {
|
||||
wp_send_json_error(['message' => __('请先登录', 'aksrt')], 401);
|
||||
}
|
||||
$html = $this->render_details_modal_html(get_current_user_id());
|
||||
wp_send_json_success(['html' => $html]);
|
||||
}
|
||||
|
||||
public function is_signed_today(int $user_id = 0): bool {
|
||||
$user_id = $user_id > 0 ? $user_id : get_current_user_id();
|
||||
if ($user_id <= 0) {
|
||||
@@ -299,12 +263,13 @@ class Signin {
|
||||
}
|
||||
|
||||
/**
|
||||
* legacy checkin_details_modal lite HTML.
|
||||
* 签到详情弹窗 HTML(参考主题 zib_get_user_checkin_details_modal 1:1):
|
||||
* 炫彩头部 + 用户卡片(我的积分/我的等级)+ 每日签到奖励卡 + 连续签到奖励 7 天迷你卡。
|
||||
*/
|
||||
public function render_details_modal_html(int $user_id): string {
|
||||
$user = get_userdata($user_id);
|
||||
if (!$user) {
|
||||
return '<div class="aksrt-checkin-modal"><p>' . esc_html__('用户不存在', 'aksrt') . '</p></div>';
|
||||
return '<div class="modal-body"><p class="muted-color padding-10 text-center">' . esc_html__('用户不存在', 'aksrt') . '</p></div>';
|
||||
}
|
||||
$cfg = class_exists('\AKSRT\Settings\Settings')
|
||||
? \AKSRT\Settings\Settings::get_signin_reward_settings()
|
||||
@@ -313,87 +278,100 @@ class Signin {
|
||||
$rec = $this->get_record($user_id, $today);
|
||||
$streak = $this->compute_current_streak($user_id, $today);
|
||||
$total = $this->get_total_days($user_id);
|
||||
$balance = \AKSRT\Ledger\LedgerService::get_instance()->balance($user_id, \AKSRT\Ledger\AssetType::POINTS);
|
||||
$base = (int) ($cfg['base'] ?? 0);
|
||||
$currency = class_exists('\AKSRT\Settings\Settings')
|
||||
? \AKSRT\Settings\Settings::get_currency_name()
|
||||
: __('积分', 'aksrt');
|
||||
$base = (int) ($cfg['base'] ?? 0);
|
||||
$avatar = function_exists('aksrt_get_avatar_url')
|
||||
? aksrt_get_avatar_url($user_id, ['size' => 64])
|
||||
: get_avatar_url($user_id, ['size' => 64]);
|
||||
$center = function_exists('aksrt_get_user_center_url')
|
||||
? aksrt_get_user_center_url('assets')
|
||||
: home_url('/');
|
||||
$points_svg = static fn(string $class = 'icon'): string =>
|
||||
'<svg class="' . esc_attr($class) . '" aria-hidden="true"><use xlink:href="#icon-points"></use></svg>';
|
||||
$gift_svg = static fn(string $class = 'icon'): string =>
|
||||
'<svg class="' . esc_attr($class) . '" aria-hidden="true"><use xlink:href="#icon-gift-color"></use></svg>';
|
||||
$signed_today = (bool) $rec;
|
||||
|
||||
$html = '<div class="aksrt-checkin-modal">';
|
||||
$html .= '<div class="aksrt-checkin-modal__head">';
|
||||
$html .= '<i class="fa fas fa-calendar-check" aria-hidden="true"></i>';
|
||||
$html .= '<strong>' . esc_html__('签到领奖励', 'aksrt') . '</strong>';
|
||||
$html .= '<button type="button" class="aksrt-modal-runtime__close" data-aksrt-modal-close aria-label="' . esc_attr__('关闭', 'aksrt') . '">×</button>';
|
||||
$html .= '</div>';
|
||||
|
||||
$html .= '<div class="aksrt-checkin-modal__user">';
|
||||
$html .= '<img class="aksrt-checkin-modal__avatar" src="' . esc_url($avatar) . '" alt="" width="40" height="40" />';
|
||||
$html .= '<div class="aksrt-checkin-modal__user-meta">';
|
||||
$html .= '<div class="aksrt-checkin-modal__name">' . esc_html($user->display_name) . '</div>';
|
||||
$html .= '<div class="aksrt-checkin-modal__sub">' . esc_html(sprintf(__('累计签到 %d 天', 'aksrt'), $total)) . '</div>';
|
||||
// 炫彩头部
|
||||
$html = '<div class="modal-body">';
|
||||
$html .= '<div class="modal-colorful-header colorful-bg c-blue">';
|
||||
$html .= '<button class="close" data-aksrt-modal-close aria-label="' . esc_attr__('关闭', 'aksrt') . '"><svg class="ic-close" aria-hidden="true"><use xlink:href="#icon-close"></use></svg></button>';
|
||||
$html .= '<div class="colorful-make"></div>';
|
||||
$html .= '<div class="text-center">';
|
||||
$html .= '<div class="em2x"><i class="fa fas fa-calendar-check" aria-hidden="true"></i></div>';
|
||||
$html .= '<div class="mt10 em12 padding-w10">' . esc_html__('签到领奖励', 'aksrt') . '</div>';
|
||||
$html .= '</div></div>';
|
||||
|
||||
$html .= '<div class="aksrt-checkin-modal__wallet">';
|
||||
$html .= '<a class="aksrt-checkin-modal__wallet-item" href="' . esc_url($center) . '">';
|
||||
$html .= '<span class="muted">' . esc_html($currency) . '</span>';
|
||||
$html .= '<b>' . esc_html(number_format_i18n($balance)) . '</b>';
|
||||
$html .= '</a></div>';
|
||||
// 用户卡片(头像/昵称 + 我的积分 + 我的等级)
|
||||
$points_svg_mini = $points_svg('px12 mr6 muted-2-color');
|
||||
$user_card = '<a rel="nofollow" class="block flex1 muted-box padding-10" href="' . esc_url(aksrt_get_user_center_url('credits')) . '">
|
||||
<div class="muted-2-color em09 mb6">' . esc_html__('我的积分', 'aksrt') . '</div>
|
||||
<div class="em12">' . $points_svg_mini . esc_html(number_format_i18n(aksrt_user_points($user_id))) . '<i class="muted-2-color px12 fa fas fa-angle-right ml6" aria-hidden="true"></i></div>
|
||||
</a>';
|
||||
|
||||
$html .= '<div class="aksrt-checkin-modal__card">';
|
||||
$html .= '<div class="aksrt-checkin-modal__card-main">';
|
||||
$html .= '<div class="aksrt-checkin-modal__card-title"><i class="fa fas fa-gift" aria-hidden="true"></i> ' . esc_html__('每日签到奖励', 'aksrt') . '</div>';
|
||||
$html .= '<div class="aksrt-checkin-modal__badge">' . esc_html($currency) . ' +' . esc_html((string) $base) . '</div>';
|
||||
$html .= '</div>';
|
||||
if ($rec) {
|
||||
$html .= '<div class="aksrt-checkin-modal__done"><i class="fa fas fa-calendar-check" aria-hidden="true"></i> ' . esc_html__('今日已签到', 'aksrt');
|
||||
$html .= '<div class="em09">+' . esc_html((string) (int) ($rec['reward'] ?? 0)) . '</div></div>';
|
||||
$level_s = class_exists('\AKSRT\User\Level')
|
||||
&& \AKSRT\User\Level::settings()['enabled']
|
||||
&& \AKSRT\User\Level::settings()['public_display'];
|
||||
if ($level_s) {
|
||||
$level_badge = \AKSRT\User\Level::get_badge_html($user_id, 'em12 mr10');
|
||||
$user_card .= '<a rel="nofollow" class="block flex1 muted-box padding-10 ml6" href="' . esc_url(aksrt_get_user_center_url('level')) . '">
|
||||
<div class="muted-2-color em09 mb6 flex jsb ac"><div>' . esc_html__('我的等级', 'aksrt') . '</div>' . $level_badge . '</div>
|
||||
<div class="em12">' . esc_html(number_format_i18n(\AKSRT\User\Level::get_integral($user_id))) . '<span class="px12 ml6 muted-2-color">' . esc_html__('经验值', 'aksrt') . '<i class="fa fas fa-angle-right ml6" aria-hidden="true"></i></span></div>
|
||||
</a>';
|
||||
}
|
||||
|
||||
$checkin_all_day = $total ? '<div class="mb6 em09">' . sprintf(__('累计签到%s天', 'aksrt'), '<badge class="c-blue">' . esc_html((string) $total) . '</badge>') . '</div>' : '';
|
||||
$avatar_img = aksrt_get_avatar_box($user_id, 'avatar-img avatar-mini mr6', false, true);
|
||||
$html .= '<div class="mb20"><div class="flex ac jsb"><div class="mb10">' . $avatar_img . esc_html($user->display_name) . '</div>' . $checkin_all_day . '</div><div class="flex ab jsb">' . $user_card . '</div></div>';
|
||||
|
||||
// 每日签到奖励卡片
|
||||
$reward_badge = $base ? '<badge class=" p2-10 mr6 c-green" data-toggle="tooltip" title="' . esc_attr(sprintf(__('积分奖励%s', 'aksrt'), $base)) . '">' . $points_svg('icon') . '+' . esc_html((string) $base) . '</badge>' : '';
|
||||
|
||||
if ($signed_today) {
|
||||
$checkin_btn = '<div class="em09 c-blue">
|
||||
<div class="mb10"><i class="fa fas fa-calendar-check mr6" aria-hidden="true"></i>' . esc_html__('今日已签到', 'aksrt') . '</div>
|
||||
<div class="px12">' . sprintf(__('积分', 'aksrt') . '+%d', (int) ($rec['reward'] ?? 0)) . '</div>
|
||||
</div>';
|
||||
} else {
|
||||
$html .= '<button type="button" class="but c-blue aksrt-checkin-modal__btn" data-signin-btn data-signin-idle-label="' . esc_attr__('立即签到', 'aksrt') . '" data-signin-done-label="' . esc_attr__('已签到', 'aksrt') . '"><span data-signin-label>' . esc_html__('立即签到', 'aksrt') . '</span></button>';
|
||||
$checkin_btn = '<button type="button" class="but c-blue ml10 p2-10 radius" data-signin-btn'
|
||||
. ' data-signin-idle-label="' . esc_attr__('立即签到', 'aksrt') . '"'
|
||||
. ' data-signin-done-label="' . esc_attr__('今日已签到', 'aksrt') . '">'
|
||||
. '<span data-signin-label>' . esc_html__('立即签到', 'aksrt') . '</span></button>';
|
||||
}
|
||||
$html .= '</div>';
|
||||
|
||||
$card_1 = '<div class="zib-widget flex ac jsb">
|
||||
<div class="mr10">
|
||||
<div class="em09 mb10">' . $gift_svg('mr6 em12') . esc_html__('每日签到奖励', 'aksrt') . '</div>
|
||||
<div class="flex ac jsb">' . $reward_badge . '</div>
|
||||
</div>
|
||||
' . $checkin_btn . '
|
||||
</div>';
|
||||
|
||||
// 连续签到奖励卡片(参考主题固定 7 天)
|
||||
$streak_table = is_array($cfg['streak'] ?? null) ? $cfg['streak'] : [];
|
||||
$max = 7;
|
||||
foreach (array_keys($streak_table) as $k) {
|
||||
$max = max($max, (int) $k);
|
||||
}
|
||||
$max = min(14, max(2, $max));
|
||||
$html .= '<div class="aksrt-checkin-modal__streak">';
|
||||
$html .= '<div class="aksrt-checkin-modal__streak-head"><span>' . esc_html__('连续签到奖励', 'aksrt') . '</span>';
|
||||
if ($streak > 1) {
|
||||
$html .= '<badge class="c-blue">' . esc_html(sprintf(__('已连续 %d 天', 'aksrt'), $streak)) . '</badge>';
|
||||
}
|
||||
$html .= '</div><div class="aksrt-checkin-modal__ladder">';
|
||||
for ($d = 1; $d <= $max; $d++) {
|
||||
$reward_continuous_day_html = $streak > 1 ? '<div class="mb6 em09">' . sprintf(__('已连续签到%s天', 'aksrt'), '<badge class="c-blue">' . esc_html((string) $streak) . '</badge>') . '</div>' : '';
|
||||
$continuous_lists = '';
|
||||
$max_day = 7;
|
||||
for ($d = 1; $d <= $max_day; $d++) {
|
||||
$extra = 0;
|
||||
foreach ($streak_table as $k => $v) {
|
||||
if ((int) $k === $d) {
|
||||
$extra = (int) $v;
|
||||
}
|
||||
}
|
||||
$pts = $base + $extra;
|
||||
$is_done = $streak >= $d;
|
||||
$html .= '<div class="aksrt-checkin-modal__day' . ($is_done ? ' is-done' : '') . '">';
|
||||
$html .= '<div class="aksrt-checkin-modal__day-n">D' . (int) $d . '</div>';
|
||||
$html .= '<div class="aksrt-checkin-modal__day-p">+' . esc_html((string) $pts) . '</div>';
|
||||
$html .= '</div>';
|
||||
}
|
||||
$html .= '</div></div>';
|
||||
$_points = $base + $extra;
|
||||
$is_ed = $signed_today && $streak >= $d;
|
||||
$_mini_card = $_points ? '<div class="badg badg-sm ' . ($is_ed ? 'b' : 'c') . '-green" data-toggle="tooltip" title="' . esc_attr(sprintf(__('积分奖励%s', 'aksrt'), $_points)) . '">' . $points_svg('icon') . '<br/>+' . esc_html((string) $_points) . '</div>' : '';
|
||||
|
||||
$dates = [];
|
||||
foreach ($this->get_recent_signins($user_id, 30) as $row) {
|
||||
if (!empty($row['signin_date'])) {
|
||||
$dates[] = (string) $row['signin_date'];
|
||||
}
|
||||
$continuous_lists .= '<div class="flex1 text-center" style="flex: 1;">
|
||||
<div class="mb6 checkin-mini-box">' . $_mini_card . '</div>
|
||||
<div class="muted-2-color smail' . ($is_ed ? ' c-blue' : '') . '">' . ($is_ed ? '<i class="fa fas fa-check-circle" aria-hidden="true"></i>' : '') . sprintf(__('第%d天', 'aksrt'), $d) . '</div>
|
||||
</div>';
|
||||
}
|
||||
$html .= '<div class="aksrt-checkin-modal__cal-title">' . esc_html__('最近 30 天', 'aksrt') . '</div>';
|
||||
$html .= '<div data-aksrt-signin-calendar=\'' . esc_attr(wp_json_encode(array_values($dates))) . '\'></div>';
|
||||
|
||||
$card_2 = '<div class="mt10">
|
||||
<div class="flex ac jsb"><div class="em09 mb10">' . $gift_svg('mr6 em12') . esc_html__('连续签到奖励', 'aksrt') . '</div>' . $reward_continuous_day_html . '</div>
|
||||
<div class="flex ac jsb">' . $continuous_lists . '</div>
|
||||
</div>';
|
||||
|
||||
$html .= $card_1;
|
||||
$html .= $card_2;
|
||||
$html .= '</div>';
|
||||
return $html;
|
||||
}
|
||||
@@ -418,29 +396,6 @@ class Signin {
|
||||
return $row ?: null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取最近 N 天的签到记录
|
||||
*
|
||||
* @param int $user_id
|
||||
* @param int $days
|
||||
* @return array
|
||||
*/
|
||||
public function get_recent_signins(int $user_id, int $days = 30): array {
|
||||
global $wpdb;
|
||||
$table = \AKSRT\Core\DBMigrator::table('signin_log');
|
||||
if (!$table) return [];
|
||||
|
||||
$from_date = (new \DateTimeImmutable("-{$days} days", wp_timezone()))->format('Y-m-d');
|
||||
|
||||
$rows = $wpdb->get_results($wpdb->prepare(
|
||||
"SELECT signin_date FROM {$table} WHERE user_id = %d AND signin_date >= %s ORDER BY signin_date ASC",
|
||||
$user_id,
|
||||
$from_date
|
||||
), ARRAY_A);
|
||||
|
||||
return $rows ?: [];
|
||||
}
|
||||
|
||||
/**
|
||||
* 算"今天之前"的连续签到天数。
|
||||
*/
|
||||
|
||||
Reference in new issue
Block a user