feat(single): align author bar follow and reward actions with the site flows

- guests get the sign-in trigger instead of a redirect to the login page,
  for both follow and wallet reward controls
- follow requests swap the label for an inline loader and block a second
  submit; the followed state uses the .actived hook
- follower counts sync through data-follow-count on the user homepage
  (the previous selector matched nothing) and only for the viewed profile
- reward button uses the secondary action colour; the reward template is
  only rendered for signed-in visitors
- drop the unused author-hero aria-disabled rule and stale wording in the
  touched comments

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
kate522andClaude Fable 5 committed 2026-11-04 13:34:34 +08:00
1 parent 8ad5ef9aff
commit d40906b4a3
13 files changed
+148 -56

No files matched your search

+16
View File
@@ -2655,6 +2655,22 @@ main.container,
.user-card.author .user-name > name > .follow {
flex-shrink: 0;
}
/* Follow control: the pending request swaps the label for an inline loader
* and blocks a second submit. The followed state is carried by .actived. */
[data-jio-follow][aria-disabled="true"] {
cursor: no-drop;
opacity: 0.6;
}
[data-jio-follow][aria-disabled="true"]:hover {
opacity: 0.6;
}
[data-jio-follow] .loading {
vertical-align: -0.1em;
}
.jio-author-hero__name,
.uc-header__name {
display: inline-flex;
+1 -1
View File
@@ -1 +1 @@
:root{--color-primary: #f04494;--color-primary-hover: #e0307e;--color-primary-active: #c9206d;--color-primary-soft: rgba(240, 68, 148, .1);--color-primary-softer: rgba(240, 68, 148, .05);--color-on-primary: #FFFFFF;--color-ink: #333333;--color-ink-2: #4e5358;--color-ink-3: #777777;--color-ink-4: #999999;--surface-page: #f5f6f7;--surface-1: #FFFFFF;--surface-2: #eeeeee;--surface-3: #e9ecef;--surface-4: #f8f8f8;--color-line: rgba(50, 50, 50, .06);--color-line-soft: rgba(0, 0, 0, .03);--color-line-stronger: rgba(50, 50, 50, .12);--color-success: #10B981;--color-warning: #F59E0B;--color-error: #EF4444;--color-info: #3B82F6;--color-accent: #18a058;--color-accent-soft: rgba(24, 160, 88, .12);--color-accent-shadow: rgba(24, 160, 88, .28);--font-display: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans SC", "PingFang SC", "Microsoft YaHei", sans-serif;--font-body: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans SC", "PingFang SC", "Microsoft YaHei", sans-serif;--font-ui: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans SC", "PingFang SC", "Microsoft YaHei", sans-serif;--font-mono: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace;--text-xs: .75rem;--text-sm: .875rem;--text-base: .875rem;--text-md: 1.125rem;--text-lg: 1.25rem;--text-xl: 1.5rem;--text-2xl: 1.875rem;--text-3xl: 2.25rem;--text-4xl: 3rem;--text-5xl: 3.75rem;--leading-tight: 1.2;--leading-snug: 1.4;--leading-normal: 1.6;--leading-relaxed: 1.6;--space-1: 4px;--space-2: 8px;--space-3: 12px;--space-4: 16px;--space-5: 20px;--space-6: 24px;--space-7: 32px;--space-8: 40px;--space-9: 48px;--space-10: 64px;--space-11: 80px;--space-12: 96px;--radius-xs: 3px;--radius-sm: 4px;--radius: 8px;--radius-lg: 8px;--radius-xl: 12px;--radius-2xl: 16px;--radius-pill: 100px;--radius-avatar: 50%;--shadow-xs: 0 1px 2px rgba(0, 0, 0, .03);--shadow-sm: 0 0 10px rgba(116, 116, 116, .08);--shadow: 0 0 10px rgba(116, 116, 116, .12);--shadow-lg: 0 12px 32px rgba(18, 18, 18, .1);--shadow-drawer: 0 18px 60px rgba(0, 0, 0, .2);--nav-height: 60px;--container: 1200px;--container-wide: 1440px;--container-read: 760px;--container-page: 880px;--sidebar-width: 311px;--content-gap: 24px;--section-gap: 24px;--sidebar-bp: 992px;--ease: cubic-bezier(.4, 0, .2, 1);--ease-out: cubic-bezier(.16, 1, .3, 1);--ease-spring: cubic-bezier(.34, 1.56, .64, 1);--dur-fast: .15s;--dur: .25s;--dur-slow: .4s;--focus-ring: 0 0 2px 3px rgba(253, 83, 161, .4);--z-dropdown: 100;--z-sticky: 200;--z-fixed: 300;--z-overlay: 400;--z-modal: 500;--z-popover: 600;--z-toast: 700;--z-tooltip: 800;--radius-btn: 4px;--btn-sm-h: 28px;--btn-md-h: 34px;--btn-lg-h: 44px;--card-padding: 15px;--card-padding-sm: 10px;--card-gap: 16px;--card-gap-lg: 20px;--color-primary-light: var(--color-primary-soft);--color-surface: var(--surface-1);--color-line-strong: var(--color-line);--color-bg-hover: var(--surface-2);--color-border-light: var(--color-line-soft);--color-fill: var(--surface-2);--color-warning-bg: #fffbe6;--color-warning-text: #ad6800;--text-secondary: var(--color-ink-3);--text-tertiary: var(--color-ink-4);--surface-active: var(--surface-2);--surface-card: var(--surface-1);--surface-hover: var(--surface-2);--surface-secondary: var(--surface-2);--radius-code: var(--radius);--radius-full: 100px;--shadow-xl: 0 0 10px rgba(116, 116, 116, .12);--space-16: 64px;--z-floating: 900;--transition-fast: .15s ease;--transition-base: .25s ease;--header-bg: var(--blur-bg);--header-color: var(--color-ink-2);--footer-bg: var(--surface-1);--footer-color: var(--color-ink-4);--blur-bg: rgba(255, 255, 255, .8);--float-btn-bg: rgba(200, 200, 200, .4);--font-heading: var(--font-ui);--radius-card: var(--radius);--radius-md: var(--radius);--color-primary-50: var(--color-primary-soft);--color-primary-dark: var(--color-primary-active);--shadow-md: var(--shadow);--mini-radius: calc(var(--radius) / 1.4);--jio-avatar-size-xs: 24px;--jio-avatar-size-sm: 32px;--jio-avatar-size: 40px;--jio-avatar-size-lg: 56px;--jio-avatar-size-xl: 80px;--jio-card-thumb-scale: 56.25%;--jio-rose: #f43f5e;--jio-rose-soft: rgba(244, 63, 94, .12);--jio-blue: #3b82f6;--jio-blue-soft: rgba(59, 130, 246, .12);--jio-green: #10b981;--jio-green-soft: rgba(16, 185, 129, .12);--jio-orange: #f97316;--jio-orange-soft:rgba(249, 115, 22, .12);--jio-purple: #8b5cf6;--jio-purple-soft:rgba(139, 92, 246, .12);--jio-cyan: #06b6d4;--jio-cyan-soft: rgba(6, 182, 212, .12);--jio-yellow: #eab308;--jio-yellow-soft:rgba(234, 179, 8, .12);--jio-red: #ef4444;--jio-red-soft: rgba(239, 68, 68, .12);--jio-teal: #14b8a6;--jio-teal-soft: rgba(20, 184, 166, .12);--jio-indigo: #6366f1;--jio-indigo-soft:rgba(99, 102, 241, .12);--jio-pink: #ec4899;--jio-pink-soft: rgba(236, 72, 153, .12);--jio-vip-gold-1: #f6d365;--jio-vip-gold-2: #fda085;--jio-grad-sunset-1: #f093fb;--jio-grad-sunset-2: #f5576c;--jio-grad-ocean-1: #4facfe;--jio-grad-ocean-2: #00f2fe;--jio-grad-forest-1Line truncated
:root{--color-primary: #f04494;--color-primary-hover: #e0307e;--color-primary-active: #c9206d;--color-primary-soft: rgba(240, 68, 148, .1);--color-primary-softer: rgba(240, 68, 148, .05);--color-on-primary: #FFFFFF;--color-ink: #333333;--color-ink-2: #4e5358;--color-ink-3: #777777;--color-ink-4: #999999;--surface-page: #f5f6f7;--surface-1: #FFFFFF;--surface-2: #eeeeee;--surface-3: #e9ecef;--surface-4: #f8f8f8;--color-line: rgba(50, 50, 50, .06);--color-line-soft: rgba(0, 0, 0, .03);--color-line-stronger: rgba(50, 50, 50, .12);--color-success: #10B981;--color-warning: #F59E0B;--color-error: #EF4444;--color-info: #3B82F6;--color-accent: #18a058;--color-accent-soft: rgba(24, 160, 88, .12);--color-accent-shadow: rgba(24, 160, 88, .28);--font-display: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans SC", "PingFang SC", "Microsoft YaHei", sans-serif;--font-body: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans SC", "PingFang SC", "Microsoft YaHei", sans-serif;--font-ui: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans SC", "PingFang SC", "Microsoft YaHei", sans-serif;--font-mono: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace;--text-xs: .75rem;--text-sm: .875rem;--text-base: .875rem;--text-md: 1.125rem;--text-lg: 1.25rem;--text-xl: 1.5rem;--text-2xl: 1.875rem;--text-3xl: 2.25rem;--text-4xl: 3rem;--text-5xl: 3.75rem;--leading-tight: 1.2;--leading-snug: 1.4;--leading-normal: 1.6;--leading-relaxed: 1.6;--space-1: 4px;--space-2: 8px;--space-3: 12px;--space-4: 16px;--space-5: 20px;--space-6: 24px;--space-7: 32px;--space-8: 40px;--space-9: 48px;--space-10: 64px;--space-11: 80px;--space-12: 96px;--radius-xs: 3px;--radius-sm: 4px;--radius: 8px;--radius-lg: 8px;--radius-xl: 12px;--radius-2xl: 16px;--radius-pill: 100px;--radius-avatar: 50%;--shadow-xs: 0 1px 2px rgba(0, 0, 0, .03);--shadow-sm: 0 0 10px rgba(116, 116, 116, .08);--shadow: 0 0 10px rgba(116, 116, 116, .12);--shadow-lg: 0 12px 32px rgba(18, 18, 18, .1);--shadow-drawer: 0 18px 60px rgba(0, 0, 0, .2);--nav-height: 60px;--container: 1200px;--container-wide: 1440px;--container-read: 760px;--container-page: 880px;--sidebar-width: 311px;--content-gap: 24px;--section-gap: 24px;--sidebar-bp: 992px;--ease: cubic-bezier(.4, 0, .2, 1);--ease-out: cubic-bezier(.16, 1, .3, 1);--ease-spring: cubic-bezier(.34, 1.56, .64, 1);--dur-fast: .15s;--dur: .25s;--dur-slow: .4s;--focus-ring: 0 0 2px 3px rgba(253, 83, 161, .4);--z-dropdown: 100;--z-sticky: 200;--z-fixed: 300;--z-overlay: 400;--z-modal: 500;--z-popover: 600;--z-toast: 700;--z-tooltip: 800;--radius-btn: 4px;--btn-sm-h: 28px;--btn-md-h: 34px;--btn-lg-h: 44px;--card-padding: 15px;--card-padding-sm: 10px;--card-gap: 16px;--card-gap-lg: 20px;--color-primary-light: var(--color-primary-soft);--color-surface: var(--surface-1);--color-line-strong: var(--color-line);--color-bg-hover: var(--surface-2);--color-border-light: var(--color-line-soft);--color-fill: var(--surface-2);--color-warning-bg: #fffbe6;--color-warning-text: #ad6800;--text-secondary: var(--color-ink-3);--text-tertiary: var(--color-ink-4);--surface-active: var(--surface-2);--surface-card: var(--surface-1);--surface-hover: var(--surface-2);--surface-secondary: var(--surface-2);--radius-code: var(--radius);--radius-full: 100px;--shadow-xl: 0 0 10px rgba(116, 116, 116, .12);--space-16: 64px;--z-floating: 900;--transition-fast: .15s ease;--transition-base: .25s ease;--header-bg: var(--blur-bg);--header-color: var(--color-ink-2);--footer-bg: var(--surface-1);--footer-color: var(--color-ink-4);--blur-bg: rgba(255, 255, 255, .8);--float-btn-bg: rgba(200, 200, 200, .4);--font-heading: var(--font-ui);--radius-card: var(--radius);--radius-md: var(--radius);--color-primary-50: var(--color-primary-soft);--color-primary-dark: var(--color-primary-active);--shadow-md: var(--shadow);--mini-radius: calc(var(--radius) / 1.4);--jio-avatar-size-xs: 24px;--jio-avatar-size-sm: 32px;--jio-avatar-size: 40px;--jio-avatar-size-lg: 56px;--jio-avatar-size-xl: 80px;--jio-card-thumb-scale: 56.25%;--jio-rose: #f43f5e;--jio-rose-soft: rgba(244, 63, 94, .12);--jio-blue: #3b82f6;--jio-blue-soft: rgba(59, 130, 246, .12);--jio-green: #10b981;--jio-green-soft: rgba(16, 185, 129, .12);--jio-orange: #f97316;--jio-orange-soft:rgba(249, 115, 22, .12);--jio-purple: #8b5cf6;--jio-purple-soft:rgba(139, 92, 246, .12);--jio-cyan: #06b6d4;--jio-cyan-soft: rgba(6, 182, 212, .12);--jio-yellow: #eab308;--jio-yellow-soft:rgba(234, 179, 8, .12);--jio-red: #ef4444;--jio-red-soft: rgba(239, 68, 68, .12);--jio-teal: #14b8a6;--jio-teal-soft: rgba(20, 184, 166, .12);--jio-indigo: #6366f1;--jio-indigo-soft:rgba(99, 102, 241, .12);--jio-pink: #ec4899;--jio-pink-soft: rgba(236, 72, 153, .12);--jio-vip-gold-1: #f6d365;--jio-vip-gold-2: #fda085;--jio-grad-sunset-1: #f093fb;--jio-grad-sunset-2: #f5576c;--jio-grad-ocean-1: #4facfe;--jio-grad-ocean-2: #00f2fe;--jio-grad-forest-1Line truncated
+16
View File
@@ -251,6 +251,22 @@ main.container,
.user-card.author .user-name > name > .follow {
flex-shrink: 0;
}
/* Follow control: the pending request swaps the label for an inline loader
* and blocks a second submit. The followed state is carried by .actived. */
[data-jio-follow][aria-disabled="true"] {
cursor: no-drop;
opacity: 0.6;
}
[data-jio-follow][aria-disabled="true"]:hover {
opacity: 0.6;
}
[data-jio-follow] .loading {
vertical-align: -0.1em;
}
.jio-author-hero__name,
.uc-header__name {
display: inline-flex;
+2 -8
View File
@@ -139,11 +139,6 @@
gap: 8px;
}
.jio-author-hero__follow-btn[aria-disabled="true"] {
opacity: 0.72;
cursor: not-allowed;
}
.jio-author-hero__message-btn {
position: relative;
}
@@ -634,9 +629,8 @@
/* 加载期收起旧页内容:翻页 / 切 tab 是整页替换语义(author.js 最终就是
panel.innerHTML = html),骨架必须独占内容区,而不是追加在旧卡片下方 ——
否则先看到「第 1 页卡片 + 脚下一摞骨架」,等接口返回才换页。
对齐参考主题 post_ajax() 的 beforeSend:replace 时 $item.remove() +
find(pag).remove()。收 display 而非删节点,仍守住「不写 inline display」契约。
否则先看到「第 1 页卡片 + 脚下一摞骨架」,等接口返回才换页。替换时旧卡片
与旧分页都先撤掉。收 display 而非删节点,仍守住「不写 inline display」契约。
只收 .jio-user-home__post-list 的**直接子级** .posts-item:骨架内层同样带
.posts-item,写成后代选择器会连骨架自己一起隐藏掉。 */
+74 -27
View File
@@ -6,68 +6,113 @@
(function () {
'use strict';
var CONFIG = window.jioData || {};
var API_ROOT = (function () {
var data = window.jioData || {};
var raw = data.apiUrl || data.restUrl || data.rest_url || '/index.php?rest_route=/jio/v1';
var raw = CONFIG.apiUrl || CONFIG.restUrl || CONFIG.rest_url || '/index.php?rest_route=/jio/v1';
return String(raw).replace(/\/$/, '');
})();
var NONCE = (window.jioData && window.jioData.restNonce) || '';
var NONCE = CONFIG.restNonce || '';
function buttonsFor(userId) {
return Array.prototype.slice.call(document.querySelectorAll('[data-jio-follow="' + userId + '"]'));
}
function setLoading(buttons, loading) {
buttons.forEach(function (button) {
button.disabled = loading;
button.classList.toggle('is-loading', loading);
});
function notify(message, type) {
if (typeof window.notyf !== 'function') return;
window.notyf(message, type || 'info');
}
/** 站点登录弹窗由 auth.js 的全局委托接管,这里只点一下触发器。 */
function openLoginModal() {
var trigger = document.querySelector('.signin-loader') ||
document.querySelector('[data-jio-auth-required]') ||
document.querySelector('#nav-login-btn');
if (trigger) trigger.click();
}
/** 待发送态:按钮置灰不可重复提交,文案换成行内加载点。 */
function setPending(button, pending) {
var label = button.querySelector('[data-jio-follow-label]');
if (pending) {
button.setAttribute('disabled', '');
button.setAttribute('aria-disabled', 'true');
if (label) {
label.dataset.jioFollowLabel = label.textContent;
label.innerHTML = '<i class="loading zts"></i>';
}
return;
}
button.removeAttribute('disabled');
button.removeAttribute('aria-disabled');
if (label && label.dataset.jioFollowLabel !== undefined) {
label.textContent = label.dataset.jioFollowLabel;
delete label.dataset.jioFollowLabel;
}
}
function updateButton(button, isFollowing) {
var icon = button.querySelector('i');
var label = button.querySelector('[data-jio-follow-label]');
if (!label) return;
button.classList.toggle('is-following', isFollowing);
button.classList.toggle('actived', isFollowing);
var icon = button.querySelector('i');
if (icon) {
icon.className = isFollowing ? 'fa fas fa-heart em09' : 'fa far fa-heart em09';
var sizeClass = /\bem09\b/.test(icon.className) ? ' em09' : '';
icon.className = (isFollowing ? 'fa fas fa-heart' : 'fa far fa-heart') + sizeClass;
}
label.textContent = isFollowing ? '已关注' : '关注';
label.textContent = isFollowing
? button.getAttribute('data-jio-follow-ok') || '已关注'
: button.getAttribute('data-jio-follow-text') || '关注';
delete label.dataset.jioFollowLabel;
button.title = isFollowing ? '点击取消关注' : '关注该用户';
}
function updateFollowerCount(count) {
var countEl = document.querySelector('.jio-user-home__stat-value[data-followers]');
if (countEl && count !== undefined) {
countEl.textContent = count;
}
/** 关注数只在浏览被关注者本人的主页时同步,避免改到别人的粉丝数。 */
function updateFollowerCount(targetUserId, count) {
if (count === undefined || count === null) return;
var scope = document.querySelector('.dashboard[data-user-id="' + targetUserId + '"]');
if (!scope) return;
scope.querySelectorAll('[data-follow-count="followers"]').forEach(function (node) {
node.textContent = count;
});
}
// 全局 document 委托只绑定一次(模块顶层执行)。
document.addEventListener('click', function (event) {
var button = event.target.closest('[data-jio-follow]');
if (!button) return;
event.preventDefault();
var targetUserId = parseInt(button.getAttribute('data-jio-follow'), 10);
if (!targetUserId || button.disabled) return;
if (!targetUserId || button.hasAttribute('disabled')) return;
var matchingButtons = buttonsFor(targetUserId);
setLoading(matchingButtons, true);
matchingButtons.forEach(function (matchingButton) {
setPending(matchingButton, true);
});
fetch(API_ROOT + '/follow/toggle', {
method: 'POST',
credentials: 'same-origin',
headers: {
'Content-Type': 'application/json',
'X-WP-Nonce': NONCE,
},
body: JSON.stringify({ target_user_id: targetUserId }),
})
.then(function (response) { return response.json(); })
.then(function (response) {
// 会话失效时直接拉起登录弹窗,不再额外弹错误提示。
if (response.status === 401 || response.status === 403) {
openLoginModal();
return null;
}
return response.json().catch(function () {
throw new Error('操作失败');
});
})
.then(function (result) {
if (!result) return;
if (!result.success || !result.data) {
notyf(result.message || '操作失败', 'danger');
notify(result.message || '操作失败', 'danger');
return;
}
@@ -75,19 +120,21 @@
matchingButtons.forEach(function (matchingButton) {
updateButton(matchingButton, isFollowing);
});
updateFollowerCount(result.data.follower_count);
updateFollowerCount(targetUserId, result.data.follower_count);
if (!isFollowing) {
var managementItem = button.closest('.uc-follow-management__item');
if (managementItem) managementItem.remove();
}
notyf(isFollowing ? '关注成功' : '已取消关注', 'success');
notify(isFollowing ? '已关注此用户' : '已取消关注此用户', 'success');
})
.catch(function () {
notyf('网络错误,请重试', 'danger');
.catch(function (error) {
notify((error && error.message) || '网络错误,请重试', 'danger');
})
.finally(function () {
setLoading(matchingButtons, false);
matchingButtons.forEach(function (matchingButton) {
setPending(matchingButton, false);
});
});
});
})();
+1 -1
View File
@@ -1 +1 @@
(function($,window2,document2){"use strict";function closeNotyf($element){$element.addClass("notyn-out"),window2.setTimeout(function(){$element.remove()},1e3)}function notyf2(message,type,time,id){var $body=jQuery(document2.body);jQuery(".notyn").length||$body.append('<div class="notyn"></div>'),type=type||"success",type==="error"&&(type="danger"),time=time||5e3,time=time<100?time*1e3:time;var idAttribute=id?' id="'+id+'"':"",$notification=jQuery('<div class="noty1"'+idAttribute+'><div class="notyf '+type+'">'+message+"</div></div>"),shouldClose=!id;id&&jQuery("#"+id).length?(jQuery("#"+id).find(".notyf").removeClass().addClass("notyf "+type).html(message),$notification=jQuery("#"+id),shouldClose=!0):jQuery(".notyn").append($notification),shouldClose&&window2.setTimeout(function(){closeNotyf($notification)},time)}jQuery(document2.body).on("click",".noty1",function(){closeNotyf(jQuery(this))}),window2.notyf=notyf2})(jQuery,window,document),(function(window2){"use strict";var allowedKinds={identity:!0,badges:!0,orders:!0,vip:!0},allowedEvents={click:!0,shell:!0,"first-content":!0,ready:!0,"action-start":!0,"action-end":!0,unauthorized:!0,error:!0},sessions=Object.create(null),sequence=0,maxSessions=100;function normalize(value,fallback){var normalized=String(value||fallback||"").toLowerCase().replace(/[^a-z0-9-]/g,"");return normalized||fallback}function now(){return window2.performance&&typeof window2.performance.now=="function"?window2.performance.now():0}function markName(session,event){return"jio-modal-"+session.kind+"-"+session.id+"-"+event}function emit(session,event){if(!(!window2.performance||typeof window2.performance.mark!="function")){var name=markName(session,event);if(window2.performance.mark(name),session.marks[event]=name,typeof window2.performance.measure=="function"&&session.marks.click&&event!=="click")try{window2.performance.measure("jio-modal-"+session.kind+"-"+session.id+"-click-to-"+event,session.marks.click,name)}catch{}}}function start(kind){var safeKind=normalize(kind,"unknown");allowedKinds[safeKind]||(safeKind="unknown"),sequence+=1;var id=String(sequence),session={id,kind:safeKind,startedAt:now(),marks:Object.create(null)};sessions[id]=session,emit(session,"click");for(var ids=Object.keys(sessions);ids.length>maxSessions;)delete sessions[ids.shift()];return id}function mark(id,event){var session=sessions[String(id||"")],safeEvent=normalize(event,"error");!session||!allowedEvents[safeEvent]||emit(session,safeEvent)}function finish(id){mark(id,"ready")}function fail(id){mark(id,"error")}window2.JioModalTelemetry={start,mark,finish,error:fail}})(window),(function(window2){"use strict";function data(){return window2.jioData||window2.jio||{}}function createIdempotencyKey(){if(window2.crypto&&typeof window2.crypto.randomUUID=="function")return window2.crypto.randomUUID();if(!window2.crypto||typeof window2.crypto.getRandomValues!="function")throw new Error("当前浏览器无法生成安全的操作标识");var bytes=new Uint8Array(16);window2.crypto.getRandomValues(bytes),bytes[6]=bytes[6]&15|64,bytes[8]=bytes[8]&63|128;var hex=Array.prototype.map.call(bytes,function(value){return value.toString(16).padStart(2,"0")}).join("");return hex.slice(0,8)+"-"+hex.slice(8,12)+"-"+hex.slice(12,16)+"-"+hex.slice(16,20)+"-"+hex.slice(20)}function restBase(){var config=data(),raw=config.restUrl||config.apiUrl||config.rest_url||"",siteUrl=config.siteUrl||config.homeUrl||window2.location.origin+"/";return window2.location.protocol==="https:"&&siteUrl.substring(0,5)==="http:"&&(siteUrl="https:"+siteUrl.substring(5)),raw?String(raw).replace(/\/$/,""):String(siteUrl).replace(/\/$/,"")+"/index.php?rest_route=/jio/v1"}function appendQuery(url2,query){if(!query)return url2;var parts=[];return typeof query=="string"?parts=query.replace(/^\?/,"").split("&").filter(Boolean):typeof query=="object"&&Object.keys(query).forEach(function(key){query[key]===void 0||query[key]===null||parts.push(encodeURIComponent(key)+"="+encodeURIComponent(String(query[key])))}),parts.length?url2+(url2.indexOf("?")>=0?"&":"?")+parts.join("&"):url2}function url(path,query){path=String(path||"");var inlineQuery="",queryIndex=path.indexOf("?");if(queryIndex>=0&&(inlineQuery=path.slice(queryIndex+1),path=path.slice(0,queryIndex)),/^https?:\/\//i.test(path))return appendQuery(path,query||inlineQuery);path.charAt(0)!=="/"&&(path="/"+path);var base=restBase(),result=(base.indexOf("rest_route=")>=0,base+path);return appendQuery(result,query||inlineQuery)}function headers(options){var config=data(),source=options&&options.headers||{},result={},nonce=config.restNonce||config.nonce||"";return Object.keys(source).forEach(function(key){result[key]=source[key]}),result.Accept||(result.Accept="application/json, text/html;q=0.9, */*;q=0.8"),nonce&&!result["X-WP-Nonce"]&&(result["X-WP-Nonce"]=nonce),options&&options.body!==void 0&&options.body!==null&&!(window2.FormData&&options.body instanceof window2.FormData)&&!result["Content-Type"]&&(result["CoLine truncated
(function($,window2,document2){"use strict";function closeNotyf($element){$element.addClass("notyn-out"),window2.setTimeout(function(){$element.remove()},1e3)}function notyf2(message,type,time,id){var $body=jQuery(document2.body);jQuery(".notyn").length||$body.append('<div class="notyn"></div>'),type=type||"success",type==="error"&&(type="danger"),time=time||5e3,time=time<100?time*1e3:time;var idAttribute=id?' id="'+id+'"':"",$notification=jQuery('<div class="noty1"'+idAttribute+'><div class="notyf '+type+'">'+message+"</div></div>"),shouldClose=!id;id&&jQuery("#"+id).length?(jQuery("#"+id).find(".notyf").removeClass().addClass("notyf "+type).html(message),$notification=jQuery("#"+id),shouldClose=!0):jQuery(".notyn").append($notification),shouldClose&&window2.setTimeout(function(){closeNotyf($notification)},time)}jQuery(document2.body).on("click",".noty1",function(){closeNotyf(jQuery(this))}),window2.notyf=notyf2})(jQuery,window,document),(function(window2){"use strict";var allowedKinds={identity:!0,badges:!0,orders:!0,vip:!0},allowedEvents={click:!0,shell:!0,"first-content":!0,ready:!0,"action-start":!0,"action-end":!0,unauthorized:!0,error:!0},sessions=Object.create(null),sequence=0,maxSessions=100;function normalize(value,fallback){var normalized=String(value||fallback||"").toLowerCase().replace(/[^a-z0-9-]/g,"");return normalized||fallback}function now(){return window2.performance&&typeof window2.performance.now=="function"?window2.performance.now():0}function markName(session,event){return"jio-modal-"+session.kind+"-"+session.id+"-"+event}function emit(session,event){if(!(!window2.performance||typeof window2.performance.mark!="function")){var name=markName(session,event);if(window2.performance.mark(name),session.marks[event]=name,typeof window2.performance.measure=="function"&&session.marks.click&&event!=="click")try{window2.performance.measure("jio-modal-"+session.kind+"-"+session.id+"-click-to-"+event,session.marks.click,name)}catch{}}}function start(kind){var safeKind=normalize(kind,"unknown");allowedKinds[safeKind]||(safeKind="unknown"),sequence+=1;var id=String(sequence),session={id,kind:safeKind,startedAt:now(),marks:Object.create(null)};sessions[id]=session,emit(session,"click");for(var ids=Object.keys(sessions);ids.length>maxSessions;)delete sessions[ids.shift()];return id}function mark(id,event){var session=sessions[String(id||"")],safeEvent=normalize(event,"error");!session||!allowedEvents[safeEvent]||emit(session,safeEvent)}function finish(id){mark(id,"ready")}function fail(id){mark(id,"error")}window2.JioModalTelemetry={start,mark,finish,error:fail}})(window),(function(window2){"use strict";function data(){return window2.jioData||window2.jio||{}}function createIdempotencyKey(){if(window2.crypto&&typeof window2.crypto.randomUUID=="function")return window2.crypto.randomUUID();if(!window2.crypto||typeof window2.crypto.getRandomValues!="function")throw new Error("当前浏览器无法生成安全的操作标识");var bytes=new Uint8Array(16);window2.crypto.getRandomValues(bytes),bytes[6]=bytes[6]&15|64,bytes[8]=bytes[8]&63|128;var hex=Array.prototype.map.call(bytes,function(value){return value.toString(16).padStart(2,"0")}).join("");return hex.slice(0,8)+"-"+hex.slice(8,12)+"-"+hex.slice(12,16)+"-"+hex.slice(16,20)+"-"+hex.slice(20)}function restBase(){var config=data(),raw=config.restUrl||config.apiUrl||config.rest_url||"",siteUrl=config.siteUrl||config.homeUrl||window2.location.origin+"/";return window2.location.protocol==="https:"&&siteUrl.substring(0,5)==="http:"&&(siteUrl="https:"+siteUrl.substring(5)),raw?String(raw).replace(/\/$/,""):String(siteUrl).replace(/\/$/,"")+"/index.php?rest_route=/jio/v1"}function appendQuery(url2,query){if(!query)return url2;var parts=[];return typeof query=="string"?parts=query.replace(/^\?/,"").split("&").filter(Boolean):typeof query=="object"&&Object.keys(query).forEach(function(key){query[key]===void 0||query[key]===null||parts.push(encodeURIComponent(key)+"="+encodeURIComponent(String(query[key])))}),parts.length?url2+(url2.indexOf("?")>=0?"&":"?")+parts.join("&"):url2}function url(path,query){path=String(path||"");var inlineQuery="",queryIndex=path.indexOf("?");if(queryIndex>=0&&(inlineQuery=path.slice(queryIndex+1),path=path.slice(0,queryIndex)),/^https?:\/\//i.test(path))return appendQuery(path,query||inlineQuery);path.charAt(0)!=="/"&&(path="/"+path);var base=restBase(),result=(base.indexOf("rest_route=")>=0,base+path);return appendQuery(result,query||inlineQuery)}function headers(options){var config=data(),source=options&&options.headers||{},result={},nonce=config.restNonce||config.nonce||"";return Object.keys(source).forEach(function(key){result[key]=source[key]}),result.Accept||(result.Accept="application/json, text/html;q=0.9, */*;q=0.8"),nonce&&!result["X-WP-Nonce"]&&(result["X-WP-Nonce"]=nonce),options&&options.body!==void 0&&options.body!==null&&!(window2.FormData&&options.body instanceof window2.FormData)&&!result["Content-Type"]&&(result["CoLine truncated
+15 -7
View File
@@ -142,12 +142,18 @@ if (!function_exists('jio_render_user_identity_chips')) {
if (!function_exists('jio_get_user_follow')) {
/**
* Render the canonical user follow control.
*
* Guests get the login trigger instead of a follow request; signed-in
* visitors get the server-rendered state that follow.js keeps in sync.
*/
function jio_get_user_follow(string $class, int $follow_id, ?string $text = null, ?string $ok_text = null, ?string $guest_text = null): string {
$current_user_id = get_current_user_id();
if ($follow_id <= 0 || $current_user_id === $follow_id) {
return '';
}
if (class_exists('\Jio\Settings\Settings') && \Jio\Settings\Settings::close_sign()) {
return '';
}
$text = $text ?? __('关注', 'jio');
$ok_text = $ok_text ?? __('已关注', 'jio');
@@ -155,18 +161,20 @@ if (!function_exists('jio_get_user_follow')) {
$class = trim($class);
if (!is_user_logged_in()) {
$login_url = function_exists('jio_get_login_url') ? jio_get_login_url() : wp_login_url();
return '<a class="' . esc_attr($class) . '" href="' . esc_url($login_url) . '"><i class="fa far fa-heart em09" aria-hidden="true"></i> <span>' . esc_html($guest_text) . '</span></a>';
// 游客点击由 auth.js 的全局委托拉起登录弹窗,不直接跳转登录页。
return '<a href="javascript:;" class="' . esc_attr($class . ' signin-loader') . '"><i class="fa far fa-heart em09" aria-hidden="true"></i> <span>' . esc_html($guest_text) . '</span></a>';
}
$is_following = class_exists('\Jio\User\Follow')
&& \Jio\User\Follow::get_instance()->is_following($current_user_id, $follow_id);
$state_class = $is_following ? ' is-following' : '';
$state_class = $is_following ? ' actived' : '';
$title = $is_following ? __('点击取消关注', 'jio') : __('关注该用户', 'jio');
$label = $is_following ? $ok_text : $text;
$icon_class = $is_following ? 'fa fas fa-heart em09' : 'fa far fa-heart em09';
// 文案随按钮下发,follow.js 切换状态时沿用同一套标签。
$labels = ' data-jio-follow-text="' . esc_attr($text) . '" data-jio-follow-ok="' . esc_attr($ok_text) . '"';
return '<a href="javascript:;" class="' . esc_attr($class . $state_class) . '" data-jio-follow="' . $follow_id . '" title="' . esc_attr($title) . '"><i class="' . esc_attr($icon_class) . '" aria-hidden="true"></i> <span data-jio-follow-label>' . esc_html($label) . '</span></a>';
return '<a href="javascript:;" class="' . esc_attr($class . $state_class) . '" data-jio-follow="' . $follow_id . '"' . $labels . ' title="' . esc_attr($title) . '"><i class="' . esc_attr($icon_class) . '" aria-hidden="true"></i> <span data-jio-follow-label>' . esc_html($label) . '</span></a>';
}
}
@@ -213,7 +221,7 @@ if (!function_exists('jio_get_user_name')) {
$display = $user->display_name;
$vip_html = '';
if (!empty($args['vip'])) {
// 禁封徽章占据 VIP 图标位(参考主题 user_name_badge 过滤器覆盖语义)。
// 禁封徽章占据 VIP 图标位(禁封态覆盖会员图标)。
$vip_html = function_exists('jio_get_user_ban_name_badge')
? jio_get_user_ban_name_badge($user_id)
: '';
@@ -561,8 +569,8 @@ if (!function_exists('jio_get_worn_medal_icon')) {
if (!function_exists('jio_is_blog_comment_hot')) {
/**
* "神评" for ordinary post comments when likes cross the fixed threshold
* (参考主题固定 10 赞,主题不再提供该阈值配置).
* "神评" for ordinary post comments when likes cross the fixed 10-like
* threshold (not configurable).
*/
function jio_is_blog_comment_hot($comment): bool {
$comment_id = $comment instanceof WP_Comment ? (int) $comment->comment_ID : (int) $comment;
+12 -1
View File
@@ -25,6 +25,17 @@ if (!function_exists('jio_get_rewards_button')) {
$settings = \Jio\Settings\Settings::get_post_page_settings();
$label = (string) ($settings['rewards_btn_text'] ?? __('赞赏', 'jio'));
$icon = function_exists('jio_icon_markup') ? jio_icon_markup('jiosvg-money') : '';
// 站内余额赞赏必须登录;游客点按钮直接拉起登录弹窗,不在提交后才报错。
if (!is_user_logged_in()) {
return sprintf(
'<a href="javascript:;" class="%1$s signin-loader">%2$s<text>%3$s</text></a>',
esc_attr($class),
$icon, // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- fixed sprite markup.
esc_html($label)
);
}
$template_id = 'jio-reward-template-' . $user_id . '-' . $post_id;
return sprintf(
'<button type="button" class="%1$s jio-reward-open" data-jio-reward-open data-template-id="%2$s" data-user-id="%3$d" data-post-id="%4$d">%5$s<text>%6$s</text></button>',
@@ -42,7 +53,7 @@ if (!function_exists('jio_render_rewards_template_for_user')) {
function jio_render_rewards_template_for_user(int $user_id, int $post_id = 0): void {
static $done = [];
$key = $user_id . ':' . $post_id;
if ($user_id <= 0 || isset($done[$key]) || !jio_rewards_enabled()) {
if ($user_id <= 0 || isset($done[$key]) || !jio_rewards_enabled() || !is_user_logged_in()) {
return;
}
$done[$key] = true;
@@ -47,7 +47,7 @@ $result = $follow
<div class="uc-follow-management__identity"><a href="<?php echo esc_url($home_url); ?>"><?php echo esc_html((string) ($item['display_name'] ?? '')); ?></a><span><?php echo esc_html(sprintf(__('关注于 %s', 'jio'), get_date_from_gmt((string) ($item['created_at'] ?? ''), get_option('date_format')))); ?></span></div>
<div class="uc-follow-management__actions">
<a class="btn-ghost btn-sm" href="<?php echo esc_url($home_url); ?>"><?php esc_html_e('主页', 'jio'); ?></a>
<?php if ($active_list === 'following') : ?><button type="button" class="btn-outline btn-sm is-following" data-jio-follow="<?php echo esc_attr((string) $member_id); ?>"><i class="fa fas fa-heart" aria-hidden="true"></i><span data-jio-follow-label><?php esc_html_e('已关注', 'jio'); ?></span></button><?php endif; ?>
<?php if ($active_list === 'following') : ?><button type="button" class="btn-outline btn-sm actived" data-jio-follow="<?php echo esc_attr((string) $member_id); ?>" data-jio-follow-text="<?php esc_attr_e('关注', 'jio'); ?>" data-jio-follow-ok="<?php esc_attr_e('已关注', 'jio'); ?>"><i class="fa fas fa-heart" aria-hidden="true"></i><span data-jio-follow-label><?php esc_html_e('已关注', 'jio'); ?></span></button><?php endif; ?>
</div>
</div>
<?php endforeach; ?>
+1 -1
View File
@@ -61,7 +61,7 @@ require_once JIO_DIR . '/templates/parts/user-shared-helpers.php';
?>
<button type="button" class="<?php echo esc_attr(trim($tab_class)); ?>" data-tab="<?php echo esc_attr($tab_key); ?>" role="tab">
<?php echo esc_html($tab_labels[$tab_key] ?? ucfirst($tab_key)); ?>
<span class="dashboard-tabs__count opacity8"><?php echo esc_html((string) $tab_count); ?></span>
<span class="dashboard-tabs__count opacity8"<?php echo $tab_key === 'follow' ? ' data-follow-count="followers"' : ''; ?>><?php echo esc_html((string) $tab_count); ?></span>
</button>
<?php endforeach; ?>
</div>
+3 -3
View File
@@ -19,7 +19,7 @@ $author_id = (int) get_the_author_meta('ID');
$author_name = get_the_author();
$author_url = function_exists('jio_get_user_home_url') ? jio_get_user_home_url($author_id) : get_author_posts_url($author_id);
// 时间显示:完全按参考主题 zib_get_post_time_tooltip 的分支顺序。
// 时间显示分支顺序:
// 1) 数据源 date → 发布时间
// 2) 非已发布状态 → 更新时间
// 3) 更新时间 > 发布时间 → 文案用更新时间+「更新」,tooltip 仍为发布时间+「发布」
@@ -43,7 +43,7 @@ if ($time_source === 'date') {
$rel_label = __('发布', 'jio');
$abs_fmt = $published_title;
} elseif (!$is_published) {
// 2) 草稿/待审等:参考固定展示更新时间。
// 2) 草稿/待审等:固定展示更新时间。
$time_mysql = $modified_mysql;
$rel_label = __('更新', 'jio');
$abs_fmt = $modified_title;
@@ -78,7 +78,7 @@ $name_html = jio_get_user_name([
]);
$follow_html = jio_get_user_follow('px12-sm ml10 follow but c-red', $author_id);
$reward_button = get_current_user_id() !== $author_id && function_exists('jio_get_rewards_button')
? jio_get_rewards_button($author_id, 'ml6 but c-yellow px12-sm', get_the_ID())
? jio_get_rewards_button($author_id, 'ml6 but c-blue px12-sm', get_the_ID())
: '';
?>
+1 -1
View File
@@ -47,7 +47,7 @@ $total_pages = max(1, (int) ($list['total_pages'] ?? 1));
<li>
<button type="button" class="uh-follow__subtab<?php echo $follow_sub === 'followers' ? ' is-active focus-color' : ' muted-color'; ?>" data-uh-sub="followers">
<?php esc_html_e('粉丝', 'jio'); ?>
<span class="uh-follow__subtab-count"><?php echo esc_html((string) $follower_count); ?></span>
<span class="uh-follow__subtab-count" data-follow-count="followers"><?php echo esc_html((string) $follower_count); ?></span>
</button>
</li>
<li>
+5 -5
View File
@@ -76,8 +76,8 @@ $show_header_notification = !empty($notification_settings['enabled'])
&& in_array('user_home_header', (array) ($notification_settings['icon_show'] ?? []), true);
$messages_url = $show_header_notification && function_exists('jio_get_messages_page_url') ? jio_get_messages_page_url() : '';
$unread_count = $is_own ? jio_get_user_unread_notification_count($user_id) : 0;
// 封面三点菜单末项:管理员 → 禁封/解封(参考主题 author_header_drop_lists 钩子语义,
// 禁封权限优先于举报);否则 → 举报用户(保持既有契约)。
// 封面三点菜单末项:管理员 → 禁封/解封(禁封权限优先于举报);
// 否则 → 举报用户(保持既有契约)。
$cover_ban_html = function_exists('jio_get_user_cover_menu_action_html')
? jio_get_user_cover_menu_action_html($user_id)
: '';
@@ -151,8 +151,8 @@ $show_cover_menu = $has_more_details || $show_cover_report || $cover_ban_html !=
<?php endif; ?>
<?php
// 治理状态徽章占据身份行元信息位(参考主题 author_header_identity 钩子:
// 红=违规已封号 / 黄=小黑屋禁封中 / 灰=禁言中,字段与颜色统一管理)。
// 治理状态徽章占据身份行元信息位:
// 红=违规已封号 / 黄=小黑屋禁封中 / 灰=禁言中,字段与颜色统一管理。
$ban_badge = function_exists('jio_get_user_ban_identity_badge')
? jio_get_user_ban_identity_badge($user_id)
: '';
@@ -194,7 +194,7 @@ $show_cover_menu = $has_more_details || $show_cover_report || $cover_ban_html !=
<?php endif; ?>
<?php else : ?>
<?php if ($show_follow) : ?>
<?php echo jio_get_user_follow('btn-outline btn-sm jio-author-hero__follow-btn', (int) $user_id, null, null, __('登录后关注', 'jio')); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
<?php echo jio_get_user_follow('btn-outline btn-sm jio-author-hero__follow-btn', (int) $user_id); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
<?php endif; ?>
<?php endif; ?>