feat(credits): check-in details modal + history; oneline/tilt widgets
Add zibll-like check-in details remote modal (REST/AJAX), history endpoint, header details link, and keep oneline posts + tilt showcase widgets from this batch.
This commit is contained in:
1 parent
3efcc440c4
commit
be4f23fd6f
5 files changed
+331
-6
No files matched your search
@@ -20,6 +20,7 @@ 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 {
|
||||
@@ -37,6 +38,20 @@ class Signin {
|
||||
return is_user_logged_in();
|
||||
},
|
||||
]);
|
||||
register_rest_route('aksrt/v1', '/signin/history', [
|
||||
'methods' => 'GET',
|
||||
'callback' => [$this, 'rest_history'],
|
||||
'permission_callback' => function () {
|
||||
return is_user_logged_in();
|
||||
},
|
||||
]);
|
||||
register_rest_route('aksrt/v1', '/signin/details', [
|
||||
'methods' => 'GET',
|
||||
'callback' => [$this, 'rest_details_html'],
|
||||
'permission_callback' => function () {
|
||||
return is_user_logged_in();
|
||||
},
|
||||
]);
|
||||
}
|
||||
|
||||
/** GET /signin/today */
|
||||
@@ -141,14 +156,187 @@ class Signin {
|
||||
}
|
||||
}
|
||||
|
||||
// Keep lightweight meta for header/badge consumers (zibll checkin_all_day).
|
||||
update_user_meta($user_id, 'aksrt_signin_' . $today_str, 1);
|
||||
$all_day = (int) get_user_meta($user_id, 'aksrt_checkin_all_day', true);
|
||||
update_user_meta($user_id, 'aksrt_checkin_all_day', $all_day + 1);
|
||||
update_user_meta($user_id, 'aksrt_checkin_continuous_day', $new_streak);
|
||||
|
||||
return new \WP_REST_Response([
|
||||
'success' => true,
|
||||
'message' => sprintf(
|
||||
/* translators: 1: reward points 2: streak days */
|
||||
__('签到成功,积分 +%1$d(连续 %2$d 天)', 'aksrt'),
|
||||
(int) $reward,
|
||||
(int) $new_streak
|
||||
),
|
||||
'streak' => $new_streak,
|
||||
'reward' => $reward,
|
||||
'balance' => class_exists('AKSRT\\Credits\\Credits') ? Credits::balance($user_id) : 0,
|
||||
'balance' => class_exists('AKSRT\Credits\Credits') ? Credits::balance($user_id) : 0,
|
||||
], 200);
|
||||
}
|
||||
|
||||
/** 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) {
|
||||
return false;
|
||||
}
|
||||
$today = $this->today_date();
|
||||
if ((bool) get_user_meta($user_id, 'aksrt_signin_' . $today, true)) {
|
||||
return true;
|
||||
}
|
||||
return (bool) $this->get_record($user_id, $today);
|
||||
}
|
||||
|
||||
public function get_total_days(int $user_id): int {
|
||||
global $wpdb;
|
||||
$table = \AKSRT\Core\DBMigrator::table('signin_log');
|
||||
if (!$table) {
|
||||
return (int) get_user_meta($user_id, 'aksrt_checkin_all_day', true);
|
||||
}
|
||||
$n = (int) $wpdb->get_var($wpdb->prepare(
|
||||
"SELECT COUNT(*) FROM {$table} WHERE user_id = %d",
|
||||
$user_id
|
||||
));
|
||||
return $n;
|
||||
}
|
||||
|
||||
/**
|
||||
* zibll checkin_details_modal lite HTML.
|
||||
*/
|
||||
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>';
|
||||
}
|
||||
$cfg = class_exists('\AKSRT\Settings\Settings')
|
||||
? \AKSRT\Settings\Settings::get_signin_reward_settings()
|
||||
: ['enabled' => false, 'base' => 0, 'streak' => []];
|
||||
$today = $this->today_date();
|
||||
$rec = $this->get_record($user_id, $today);
|
||||
$streak = $this->compute_current_streak($user_id, $today);
|
||||
$total = $this->get_total_days($user_id);
|
||||
$balance = class_exists('\AKSRT\Credits\Credits') ? (int) Credits::balance($user_id) : 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('/');
|
||||
|
||||
$html = '<div class="aksrt-checkin-modal">';
|
||||
$html .= '<div class="aksrt-checkin-modal__head">';
|
||||
$html .= '<i class="fa fa-calendar-check-o" aria-hidden="true"></i>';
|
||||
$html .= '<strong>' . esc_html__('签到领奖励', 'aksrt') . '</strong>';
|
||||
$html .= '<button type="button" class="aksrt-remote-modal__close" data-aksrt-remote-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></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>';
|
||||
|
||||
$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="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 fa-calendar-check-o" aria-hidden="true"></i> ' . esc_html__('今日已签到', 'aksrt');
|
||||
$html .= '<div class="em09">+' . esc_html((string) (int) ($rec['reward'] ?? 0)) . '</div></div>';
|
||||
} else {
|
||||
$html .= '<button type="button" class="but c-blue aksrt-checkin-modal__btn" data-aksrt-checkin="1">' . esc_html__('立即签到', 'aksrt') . '</button>';
|
||||
}
|
||||
$html .= '</div>';
|
||||
|
||||
$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++) {
|
||||
$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>';
|
||||
|
||||
$dates = [];
|
||||
foreach ($this->get_recent_signins($user_id, 30) as $row) {
|
||||
if (!empty($row['signin_date'])) {
|
||||
$dates[] = (string) $row['signin_date'];
|
||||
}
|
||||
}
|
||||
$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>';
|
||||
$html .= '</div>';
|
||||
return $html;
|
||||
}
|
||||
|
||||
// ---------- 工具方法 ----------
|
||||
|
||||
/** 站点时区下的今日日期 'Y-m-d' */
|
||||
|
||||
Reference in new issue
Block a user