fix(user-home): add reference tab loading skeletons
This commit is contained in:
1 parent
4c6b02a6b9
commit
5a17b399a0
7 files changed
+161
-7
No files matched your search
@@ -516,6 +516,53 @@
|
|||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.aksrt-user-home .dashboard-panel .uh-follow__list--placeholder {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.aksrt-user-home .dashboard-panel.is-loading .uh-follow__list--placeholder {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.aksrt-user-home .dashboard-panel.is-loading .uh-follow__subtabs,
|
||||||
|
.aksrt-user-home .dashboard-panel.is-loading .uh-follow__list:not(.uh-follow__list--placeholder),
|
||||||
|
.aksrt-user-home .dashboard-panel.is-loading .uh-pagination,
|
||||||
|
.aksrt-user-home .dashboard-panel.is-loading .profile-empty {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.aksrt-user-home .dashboard-panel.is-load-error .uh-follow__list--placeholder {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.aksrt-user-home .uh-panel-load-error {
|
||||||
|
display: none;
|
||||||
|
margin: 12px 0;
|
||||||
|
color: var(--color-ink-4);
|
||||||
|
font-size: 13px;
|
||||||
|
text-align: center;
|
||||||
|
grid-column: 1 / -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.aksrt-user-home .dashboard-panel.is-load-error .uh-panel-load-error {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.aksrt-user-home .uh-panel-load-error button {
|
||||||
|
margin-left: 8px;
|
||||||
|
padding: 0;
|
||||||
|
border: 0;
|
||||||
|
color: var(--color-primary);
|
||||||
|
background: transparent;
|
||||||
|
font: inherit;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.aksrt-user-home .uh-follow__placeholder {
|
||||||
|
display: inline-flex;
|
||||||
|
margin: 5px 8px;
|
||||||
|
}
|
||||||
|
|
||||||
.aksrt-user-home .uh-follow__pagination {
|
.aksrt-user-home .uh-follow__pagination {
|
||||||
margin-top: 16px;
|
margin-top: 16px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
@@ -608,6 +655,21 @@
|
|||||||
animation: aksrtFadeIn 0.2s ease both;
|
animation: aksrtFadeIn 0.2s ease both;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.aksrt-user-home .dashboard-panel.is-load-error .post_ajax_loader {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.aksrt-user-home .dashboard-panel.is-load-error .aksrt-user-home__post-list[data-view="list"] > .post_ajax_loader {
|
||||||
|
display: grid;
|
||||||
|
gap: 16px;
|
||||||
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||||
|
align-items: start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.aksrt-user-home .dashboard-panel.is-load-error .placeholder {
|
||||||
|
animation: none;
|
||||||
|
}
|
||||||
|
|
||||||
@media (prefers-reduced-motion: reduce) {
|
@media (prefers-reduced-motion: reduce) {
|
||||||
.aksrt-user-home .dashboard-panel .post_ajax_loader .placeholder {
|
.aksrt-user-home .dashboard-panel .post_ajax_loader .placeholder {
|
||||||
animation: none;
|
animation: none;
|
||||||
|
|||||||
+22
-1
@@ -97,6 +97,7 @@
|
|||||||
trigger.setAttribute('aria-busy', 'true');
|
trigger.setAttribute('aria-busy', 'true');
|
||||||
trigger.textContent = '加载中…';
|
trigger.textContent = '加载中…';
|
||||||
}
|
}
|
||||||
|
panel.classList.remove('is-load-error');
|
||||||
panel.classList.add('is-loading');
|
panel.classList.add('is-loading');
|
||||||
|
|
||||||
var url = restBase.replace(/\/$/, '') + '/user-home/panel/' + userId + '/' + encodeURIComponent(name)
|
var url = restBase.replace(/\/$/, '') + '/user-home/panel/' + userId + '/' + encodeURIComponent(name)
|
||||||
@@ -117,10 +118,14 @@
|
|||||||
} else {
|
} else {
|
||||||
panel.innerHTML = payload.data.html;
|
panel.innerHTML = payload.data.html;
|
||||||
}
|
}
|
||||||
|
panel.dataset.panelLoaded = 'true';
|
||||||
if (window.jQuery) jQuery(document).trigger('post_ajax.ed', [panel]);
|
if (window.jQuery) jQuery(document).trigger('post_ajax.ed', [panel]);
|
||||||
if (typeof window.auto_fun === 'function') window.auto_fun();
|
if (typeof window.auto_fun === 'function') window.auto_fun();
|
||||||
});
|
});
|
||||||
}).catch(function (error) {
|
}).catch(function (error) {
|
||||||
|
if (panel.dataset.panelLoaded === 'false') {
|
||||||
|
panel.classList.add('is-load-error');
|
||||||
|
}
|
||||||
if (appendComments && trigger && trigger.isConnected) {
|
if (appendComments && trigger && trigger.isConnected) {
|
||||||
trigger.disabled = false;
|
trigger.disabled = false;
|
||||||
trigger.removeAttribute('aria-busy');
|
trigger.removeAttribute('aria-busy');
|
||||||
@@ -141,7 +146,12 @@
|
|||||||
|
|
||||||
tabs.forEach(function (tab) {
|
tabs.forEach(function (tab) {
|
||||||
tab.addEventListener('click', function () {
|
tab.addEventListener('click', function () {
|
||||||
switchTo(tab.getAttribute('data-tab'));
|
var name = tab.getAttribute('data-tab');
|
||||||
|
switchTo(name);
|
||||||
|
var panel = panels[name] && panels[name].panel;
|
||||||
|
if (panel && panel.dataset.panelLoaded === 'false') {
|
||||||
|
loadPanelBody(panel, name, 1, 'followers').catch(notifyLoadError);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -161,6 +171,17 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var retryBtn = target.closest('[data-uh-retry]');
|
||||||
|
if (retryBtn) {
|
||||||
|
event.preventDefault();
|
||||||
|
var retryPanel = retryBtn.closest('.dashboard-panel');
|
||||||
|
if (!retryPanel) return;
|
||||||
|
var retryName = panelNameOf(retryPanel);
|
||||||
|
if (!retryName) return;
|
||||||
|
loadPanelBody(retryPanel, retryName, 1, 'followers').catch(notifyLoadError);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
var commentsNext = target.closest('[data-uh-comments-next]');
|
var commentsNext = target.closest('[data-uh-comments-next]');
|
||||||
if (commentsNext) {
|
if (commentsNext) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|||||||
@@ -190,6 +190,23 @@ if (!function_exists('aksrt_render_user_home_pagination')) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!function_exists('aksrt_get_user_home_follow_placeholder')) {
|
||||||
|
/**
|
||||||
|
* Skeleton cards used while the public home follow panel is loading.
|
||||||
|
* Mirrors zib_get_author_tab_loader('follow').
|
||||||
|
*/
|
||||||
|
function aksrt_get_user_home_follow_placeholder(): string {
|
||||||
|
$item = '<div class="author-minicard radius8 flex ac uh-follow__placeholder" aria-hidden="true">'
|
||||||
|
. '<div class="avatar-img mr10"><div class="avatar placeholder"></div></div>'
|
||||||
|
. '<div class="flex1"><div class="placeholder k1 mb6"></div>'
|
||||||
|
. '<i><i class="placeholder s1"></i><i class="placeholder s1 ml10"></i></i></div></div>';
|
||||||
|
|
||||||
|
return '<div class="uh-follow__list uh-follow__list--placeholder" role="status" aria-label="' . esc_attr__('正在加载', 'aksrt') . '">'
|
||||||
|
. str_repeat($item, 6)
|
||||||
|
. '</div>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!function_exists('aksrt_capture_user_home_panel')) {
|
if (!function_exists('aksrt_capture_user_home_panel')) {
|
||||||
/**
|
/**
|
||||||
* 捕获公开用户主页面板 body(templates/parts/user-home/panel-{tab}.php)。
|
* 捕获公开用户主页面板 body(templates/parts/user-home/panel-{tab}.php)。
|
||||||
|
|||||||
@@ -71,9 +71,14 @@ require_once AKSRT_DIR . '/templates/parts/user-shared-helpers.php';
|
|||||||
if (!locate_template(['templates/parts/user-home/panel-' . $panel_tab . '.php'], false, false)) {
|
if (!locate_template(['templates/parts/user-home/panel-' . $panel_tab . '.php'], false, false)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
$defer_panel = in_array($panel_tab, ['favorites', 'follow'], true) && $panel_tab !== $active_tab;
|
||||||
|
$panel_context = $ctx;
|
||||||
|
if ($defer_panel) {
|
||||||
|
$panel_context['placeholder_only'] = true;
|
||||||
|
}
|
||||||
?>
|
?>
|
||||||
<div id="panel-<?php echo esc_attr($panel_tab); ?>" class="dashboard-panel<?php echo $panel_tab === $active_tab ? ' is-active' : ''; ?>" role="tabpanel"<?php echo $panel_tab !== $active_tab ? ' hidden' : ''; ?>>
|
<div id="panel-<?php echo esc_attr($panel_tab); ?>" class="dashboard-panel<?php echo $panel_tab === $active_tab ? ' is-active' : ''; ?>" role="tabpanel" data-panel-loaded="<?php echo $defer_panel ? 'false' : 'true'; ?>"<?php echo $panel_tab !== $active_tab ? ' hidden' : ''; ?>>
|
||||||
<?php get_template_part('templates/parts/user-home/panel', $panel_tab, $ctx); ?>
|
<?php get_template_part('templates/parts/user-home/panel', $panel_tab, $panel_context); ?>
|
||||||
</div>
|
</div>
|
||||||
<?php endforeach; ?>
|
<?php endforeach; ?>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -14,15 +14,34 @@ defined('ABSPATH') || exit;
|
|||||||
$args = is_array($args ?? null) ? $args : [];
|
$args = is_array($args ?? null) ? $args : [];
|
||||||
extract($args, EXTR_SKIP); // phpcs:ignore WordPress.PHP.DontExtract.extract_extract
|
extract($args, EXTR_SKIP); // phpcs:ignore WordPress.PHP.DontExtract.extract_extract
|
||||||
|
|
||||||
if (!empty($favorite_post_ids)) :
|
$user_home_render_config = aksrt_resolve_post_list_render_config([
|
||||||
$user_home_render_config = aksrt_resolve_post_list_render_config([
|
|
||||||
'context' => 'author',
|
'context' => 'author',
|
||||||
'overrides' => [
|
'overrides' => [
|
||||||
'no_author' => true,
|
'no_author' => true,
|
||||||
'no_margin' => true,
|
'no_margin' => true,
|
||||||
],
|
],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
if (!empty($placeholder_only)) {
|
||||||
|
$placeholder_mode = (string) ($user_home_render_config['mode'] ?? 'list');
|
||||||
|
$placeholder_html = function_exists('aksrt_render_placeholder')
|
||||||
|
? aksrt_render_placeholder($placeholder_mode, 4)
|
||||||
|
: '';
|
||||||
|
?>
|
||||||
|
<div class="posts-row ajaxpager aksrt-user-home__post-list" data-view="<?php echo esc_attr($placeholder_mode); ?>">
|
||||||
|
<div class="post_ajax_loader" role="status" aria-label="<?php esc_attr_e('正在加载', 'aksrt'); ?>">
|
||||||
|
<?php echo $placeholder_html; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- 固定文章骨架由 aksrt_render_placeholder() 输出。 ?>
|
||||||
|
</div>
|
||||||
|
<div class="uh-panel-load-error" role="alert">
|
||||||
|
<span><?php esc_html_e('加载失败,请重试', 'aksrt'); ?></span>
|
||||||
|
<button type="button" data-uh-retry><?php esc_html_e('重试', 'aksrt'); ?></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<?php
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($favorite_post_ids)) :
|
||||||
$favorites_query = new WP_Query([
|
$favorites_query = new WP_Query([
|
||||||
'post_type' => 'post',
|
'post_type' => 'post',
|
||||||
'post_status' => 'publish',
|
'post_status' => 'publish',
|
||||||
|
|||||||
@@ -21,6 +21,17 @@ extract($args, EXTR_SKIP); // phpcs:ignore WordPress.PHP.DontExtract.extract_ext
|
|||||||
$follow_page = max(1, (int) ($follow_page ?? 1));
|
$follow_page = max(1, (int) ($follow_page ?? 1));
|
||||||
$follow_sub = in_array($follow_sub ?? 'followers', ['following', 'followers'], true) ? $follow_sub : 'followers';
|
$follow_sub = in_array($follow_sub ?? 'followers', ['following', 'followers'], true) ? $follow_sub : 'followers';
|
||||||
|
|
||||||
|
if (!empty($placeholder_only)) :
|
||||||
|
?>
|
||||||
|
<?php echo aksrt_get_user_home_follow_placeholder(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- 固定骨架由本主题 helper 输出。 ?>
|
||||||
|
<div class="uh-panel-load-error" role="alert">
|
||||||
|
<span><?php esc_html_e('加载失败,请重试', 'aksrt'); ?></span>
|
||||||
|
<button type="button" data-uh-retry><?php esc_html_e('重试', 'aksrt'); ?></button>
|
||||||
|
</div>
|
||||||
|
<?php
|
||||||
|
return;
|
||||||
|
endif;
|
||||||
|
|
||||||
$follow_instance = class_exists('\AKSRT\User\Follow') ? \AKSRT\User\Follow::get_instance() : null;
|
$follow_instance = class_exists('\AKSRT\User\Follow') ? \AKSRT\User\Follow::get_instance() : null;
|
||||||
$following_count = $follow_instance ? (int) $follow_instance->get_following_count((int) $user_id) : 0;
|
$following_count = $follow_instance ? (int) $follow_instance->get_following_count((int) $user_id) : 0;
|
||||||
$follower_count = $follow_instance ? (int) $follow_instance->get_follower_count((int) $user_id) : 0;
|
$follower_count = $follow_instance ? (int) $follow_instance->get_follower_count((int) $user_id) : 0;
|
||||||
@@ -63,3 +74,4 @@ $total_pages = max(1, (int) ($list['total_pages'] ?? 1));
|
|||||||
aksrt_render_profile_empty($svg_empty_follow ?? $svg_empty_post ?? '', $empty_msg);
|
aksrt_render_profile_empty($svg_empty_follow ?? $svg_empty_post ?? '', $empty_msg);
|
||||||
?>
|
?>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
|
<?php echo aksrt_get_user_home_follow_placeholder(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- 固定骨架由本主题 helper 输出。 ?>
|
||||||
@@ -570,16 +570,24 @@ test('public user home stays param-free and swaps panels via REST fragments', ()
|
|||||||
|
|
||||||
// Shell mounts panel wrappers from the tab registry with a user id hook.
|
// Shell mounts panel wrappers from the tab registry with a user id hook.
|
||||||
assert.match(shell, /data-user-id="<\?php echo \(int\) \$user_id; \?>">/);
|
assert.match(shell, /data-user-id="<\?php echo \(int\) \$user_id; \?>">/);
|
||||||
assert.match(shell, /get_template_part\('templates\/parts\/user-home\/panel', \$panel_tab, \$ctx\)/);
|
assert.match(shell, /in_array\(\$panel_tab, \['favorites', 'follow'\], true\) && \$panel_tab !== \$active_tab/);
|
||||||
|
assert.match(shell, /data-panel-loaded="<\?php echo \$defer_panel \? 'false' : 'true'; \?>"/);
|
||||||
|
assert.match(shell, /get_template_part\('templates\/parts\/user-home\/panel', \$panel_tab, \$panel_context\)/);
|
||||||
|
|
||||||
// Follow panel: button-driven sub-tabs, pagination without URL building.
|
// Follow panel: button-driven sub-tabs, pagination without URL building.
|
||||||
assert.match(follow, /data-uh-sub="followers"/);
|
assert.match(follow, /data-uh-sub="followers"/);
|
||||||
assert.match(follow, /data-uh-sub="following"/);
|
assert.match(follow, /data-uh-sub="following"/);
|
||||||
assert.match(follow, /aksrt_render_user_home_pagination\(\$total_pages, \$follow_page\)/);
|
assert.match(follow, /aksrt_render_user_home_pagination\(\$total_pages, \$follow_page\)/);
|
||||||
|
assert.match(ctx, /function aksrt_get_user_home_follow_placeholder\(\): string[\s\S]*?author-minicard radius8 flex ac uh-follow__placeholder[\s\S]*?placeholder k1 mb6[\s\S]*?placeholder s1/);
|
||||||
|
assert.match(ctx, /str_repeat\(\$item, 6\)/);
|
||||||
|
assert.match(follow, /aksrt_get_user_home_follow_placeholder\(\)/);
|
||||||
|
assert.match(follow, /data-uh-retry[\s\S]*?重试/);
|
||||||
assert.doesNotMatch(follow, /add_query_arg|\$_GET|paginate_links/);
|
assert.doesNotMatch(follow, /add_query_arg|\$_GET|paginate_links/);
|
||||||
|
|
||||||
assert.match(comments, /data-uh-comments-next=/);
|
assert.match(comments, /data-uh-comments-next=/);
|
||||||
assert.match(favorites, /aksrt_render_user_home_pagination\(/);
|
assert.match(favorites, /aksrt_render_user_home_pagination\(/);
|
||||||
|
assert.match(favorites, /if \(!empty\(\$placeholder_only\)\)[\s\S]*?aksrt_render_placeholder\(\$placeholder_mode, 4\)/);
|
||||||
|
assert.match(favorites, /data-uh-retry[\s\S]*?重试/);
|
||||||
assert.doesNotMatch(comments + favorites, /add_query_arg|\$_GET/);
|
assert.doesNotMatch(comments + favorites, /add_query_arg|\$_GET/);
|
||||||
|
|
||||||
// REST fragment endpoint serves public panel bodies.
|
// REST fragment endpoint serves public panel bodies.
|
||||||
@@ -593,6 +601,16 @@ test('public user home stays param-free and swaps panels via REST fragments', ()
|
|||||||
assert.match(authorJs, /data-uh-sub/);
|
assert.match(authorJs, /data-uh-sub/);
|
||||||
assert.match(authorJs, /post_ajax\.ed/);
|
assert.match(authorJs, /post_ajax\.ed/);
|
||||||
assert.match(authorJs, /window\.auto_fun\(\)/);
|
assert.match(authorJs, /window\.auto_fun\(\)/);
|
||||||
|
assert.match(authorJs, /panel\.dataset\.panelLoaded === 'false'[\s\S]*?loadPanelBody\(panel, name, 1, 'followers'\)/);
|
||||||
|
assert.match(authorJs, /target\.closest\('\[data-uh-retry\]'\)[\s\S]*?loadPanelBody\(retryPanel, retryName, 1, 'followers'\)/);
|
||||||
|
assert.match(authorJs, /panel\.dataset\.panelLoaded = 'true'/);
|
||||||
|
assert.match(authorJs, /panel\.classList\.add\('is-load-error'\)/);
|
||||||
|
const userHomeCss = read('assets/css/routes/user-home.css');
|
||||||
|
assert.match(userHomeCss, /dashboard-panel\.is-loading \.uh-follow__list--placeholder\s*\{\s*display: block;/);
|
||||||
|
assert.match(userHomeCss, /dashboard-panel\.is-loading \.uh-follow__subtabs,[\s\S]*?dashboard-panel\.is-loading \.uh-follow__list:not\(\.uh-follow__list--placeholder\),[\s\S]*?dashboard-panel\.is-loading \.uh-pagination,[\s\S]*?dashboard-panel\.is-loading \.profile-empty\s*\{\s*display: none;/);
|
||||||
|
assert.match(userHomeCss, /dashboard-panel\.is-load-error \.uh-follow__list--placeholder\s*\{\s*display: block;/);
|
||||||
|
assert.match(userHomeCss, /dashboard-panel\.is-load-error \.post_ajax_loader\s*\{\s*display: block;/);
|
||||||
|
assert.match(userHomeCss, /dashboard-panel\.is-load-error \.uh-panel-load-error\s*\{\s*display: block;/);
|
||||||
|
|
||||||
// 翻页拿子分组只读**当前面板内**的 is-active 子页签,不读 panel.dataset:
|
// 翻页拿子分组只读**当前面板内**的 is-active 子页签,不读 panel.dataset:
|
||||||
// uhSub 是粉丝面板私有状态,写满所有面板会互相串味 —— 先点过粉丝子页签,
|
// uhSub 是粉丝面板私有状态,写满所有面板会互相串味 —— 先点过粉丝子页签,
|
||||||
|
|||||||
Reference in new issue
Block a user