成就
This commit is contained in:
parent
1efd1b779c
commit
2d1bd5cb52
@ -222,6 +222,13 @@
|
||||
border: 1px solid var(--achievement-card-border);
|
||||
background: var(--achievement-card-purple);
|
||||
}
|
||||
.badge-card:focus,
|
||||
.badge-card:focus-visible,
|
||||
.badge-card:active {
|
||||
outline: none;
|
||||
box-shadow: none;
|
||||
transform: none;
|
||||
}
|
||||
.badge-card::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
@ -300,20 +307,21 @@
|
||||
.footer-text {
|
||||
margin-top: 24px;
|
||||
}
|
||||
.loading {
|
||||
.achievement-loading {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 320px;
|
||||
z-index: 7;
|
||||
display: none;
|
||||
width: 375px;
|
||||
color: rgba(255, 255, 255, 0.72);
|
||||
font-size: 12px;
|
||||
line-height: 16px;
|
||||
text-align: center;
|
||||
}
|
||||
.loading.active {
|
||||
display: block;
|
||||
.achievement-loading .hy-loading__mascot {
|
||||
width: 76px;
|
||||
height: 76px;
|
||||
}
|
||||
.achievement-loading .hy-loading__spinner {
|
||||
border-color: rgba(255, 255, 255, 0.22);
|
||||
border-top-color: #ffffff;
|
||||
}
|
||||
.toast {
|
||||
position: fixed;
|
||||
@ -504,21 +512,14 @@
|
||||
></button>
|
||||
</nav>
|
||||
<section class="profile" aria-label="User">
|
||||
<img
|
||||
id="profileAvatar"
|
||||
class="profile-avatar"
|
||||
src="assets/figma/profile-avatar.png"
|
||||
alt=""
|
||||
/>
|
||||
<img id="profileAvatar" class="profile-avatar" alt="" />
|
||||
<div
|
||||
id="profileFrame"
|
||||
class="profile-frame"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<img src="assets/figma/header-frame.png" alt="" />
|
||||
</div>
|
||||
></div>
|
||||
</section>
|
||||
<div id="userName" class="user-name">Name</div>
|
||||
<div id="userName" class="user-name"></div>
|
||||
<div id="earnedText" class="earned">
|
||||
Earn <strong id="earnedCount">0</strong> achievements
|
||||
</div>
|
||||
@ -561,10 +562,17 @@
|
||||
</section>
|
||||
<div
|
||||
id="loading"
|
||||
class="loading"
|
||||
data-i18n="achievement.loading"
|
||||
class="hy-loading achievement-loading"
|
||||
aria-hidden="true"
|
||||
>
|
||||
Loading...
|
||||
<div class="hy-loading__mascot" aria-hidden="true"></div>
|
||||
<div class="hy-loading__spinner" aria-hidden="true"></div>
|
||||
<div
|
||||
class="hy-loading__text"
|
||||
data-i18n="achievement.loading"
|
||||
>
|
||||
Loading...
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
@ -641,73 +649,6 @@
|
||||
function validTab(value) {
|
||||
return tabs[value] ? value : 'honors';
|
||||
}
|
||||
function isLocalOrigin() {
|
||||
var host = window.location.hostname;
|
||||
return (
|
||||
host === '127.0.0.1' ||
|
||||
host === 'localhost' ||
|
||||
host === '::1'
|
||||
);
|
||||
}
|
||||
function previewMode() {
|
||||
if (!isLocalOrigin()) return '';
|
||||
return String(
|
||||
new URLSearchParams(window.location.search).get(
|
||||
'preview'
|
||||
) || ''
|
||||
).toLowerCase();
|
||||
}
|
||||
function previewAchievement(index, unlocked) {
|
||||
return {
|
||||
definition: {
|
||||
achievement_id: 'preview_' + index,
|
||||
title: 'Pirple Pearl',
|
||||
description:
|
||||
'Complete the required achievement goal to collect this badge.',
|
||||
display_config_json: JSON.stringify({
|
||||
owner_count: 10,
|
||||
}),
|
||||
primary_badge_resource: {
|
||||
name: 'Pirple Pearl',
|
||||
preview_url:
|
||||
ASSET +
|
||||
(unlocked
|
||||
? 'badge-gold.png'
|
||||
: 'badge-silver.png'),
|
||||
},
|
||||
},
|
||||
user_status: unlocked ? 'unlocked' : 'in_progress',
|
||||
unlocked_at_ms: unlocked
|
||||
? new Date('2026-01-01T00:00:00Z').getTime()
|
||||
: 0,
|
||||
};
|
||||
}
|
||||
function applyLocalPreview() {
|
||||
var mode = previewMode();
|
||||
if (!mode) return false;
|
||||
var sample = [0, 1, 2, 3, 4, 5, 6, 7, 8].map(
|
||||
function (_, index) {
|
||||
return previewAchievement(
|
||||
index + 1,
|
||||
mode === 'unlocked' ||
|
||||
(mode !== 'locked' &&
|
||||
(index === 0 || index >= 3))
|
||||
);
|
||||
}
|
||||
);
|
||||
state.profile = {
|
||||
name: 'Name',
|
||||
avatar: ASSET + 'profile-avatar.png',
|
||||
};
|
||||
state.achievements.honors = mode === 'empty' ? [] : sample;
|
||||
state.achievements.event = [];
|
||||
state.loaded.honors = true;
|
||||
state.loaded.event = true;
|
||||
render();
|
||||
if (mode === 'locked') openModal(sample[1]);
|
||||
if (mode === 'unlocked') openModal(sample[0]);
|
||||
return true;
|
||||
}
|
||||
function showToast(message) {
|
||||
var toast = byId('toast');
|
||||
toast.textContent = message;
|
||||
@ -759,7 +700,7 @@
|
||||
profile.displayUserId ||
|
||||
profile.user_id ||
|
||||
profile.userId ||
|
||||
'Name'
|
||||
''
|
||||
);
|
||||
}
|
||||
function profileAvatar(profile) {
|
||||
@ -822,17 +763,24 @@
|
||||
node.src = fallback;
|
||||
}
|
||||
};
|
||||
if (!next) {
|
||||
node.removeAttribute('src');
|
||||
return;
|
||||
}
|
||||
if (node.getAttribute('src') !== next) node.src = next;
|
||||
}
|
||||
function renderStaticFrame(node, src) {
|
||||
if (!src) {
|
||||
node.innerHTML = '';
|
||||
return;
|
||||
}
|
||||
node.innerHTML =
|
||||
'<img src="' + escapeHTML(src) + '" alt="">';
|
||||
var img = node.querySelector('img');
|
||||
img.onerror = function () {
|
||||
img.onerror = null;
|
||||
if (src === ASSET + 'header-frame.png') return;
|
||||
state.profileFrameURL = ASSET + 'header-frame.png';
|
||||
renderStaticFrame(node, ASSET + 'header-frame.png');
|
||||
state.profileFrameURL = '';
|
||||
renderStaticFrame(node, '');
|
||||
};
|
||||
}
|
||||
function isDynamicEffect(url) {
|
||||
@ -847,9 +795,7 @@
|
||||
function renderProfileFrame(profile) {
|
||||
var node = byId('profileFrame');
|
||||
if (!node) return;
|
||||
var url =
|
||||
frameURL(profileFrameData(profile)) ||
|
||||
ASSET + 'header-frame.png';
|
||||
var url = frameURL(profileFrameData(profile));
|
||||
if (state.profileFrameURL === url) return;
|
||||
state.profileFrameURL = url;
|
||||
if (state.profileFramePlayer) {
|
||||
@ -863,8 +809,8 @@
|
||||
waitForWindowLoad: false,
|
||||
});
|
||||
state.profileFramePlayer.play(url).catch(function () {
|
||||
state.profileFrameURL = ASSET + 'header-frame.png';
|
||||
renderStaticFrame(node, ASSET + 'header-frame.png');
|
||||
state.profileFrameURL = '';
|
||||
renderStaticFrame(node, '');
|
||||
});
|
||||
return;
|
||||
}
|
||||
@ -892,8 +838,8 @@
|
||||
resource.preview_url ||
|
||||
resource.asset_url ||
|
||||
resource.animation_url ||
|
||||
ASSET +
|
||||
(unlocked ? 'badge-gold.png' : 'badge-silver.png')
|
||||
resource.url ||
|
||||
''
|
||||
);
|
||||
}
|
||||
function isUnlocked(item) {
|
||||
@ -907,7 +853,7 @@
|
||||
function titleOf(item) {
|
||||
var resource = resourceOf(item);
|
||||
var def = definitionOf(item);
|
||||
return def.title || resource.name || 'Pirple Pearl';
|
||||
return def.title || resource.name || '';
|
||||
}
|
||||
function descriptionOf(item) {
|
||||
var config = configOf(item);
|
||||
@ -997,11 +943,7 @@
|
||||
function renderHeader() {
|
||||
var profile = state.profile || {};
|
||||
byId('userName').textContent = profileName(profile);
|
||||
setImage(
|
||||
byId('profileAvatar'),
|
||||
profileAvatar(profile),
|
||||
ASSET + 'profile-avatar.png'
|
||||
);
|
||||
setImage(byId('profileAvatar'), profileAvatar(profile), '');
|
||||
renderProfileFrame(profile);
|
||||
byId('earnedText').innerHTML = earnedText(unlockedCount());
|
||||
}
|
||||
@ -1017,9 +959,9 @@
|
||||
'" aria-label="' +
|
||||
escapeHTML(titleOf(item)) +
|
||||
'">' +
|
||||
'<img src="' +
|
||||
escapeHTML(img) +
|
||||
'" alt="">' +
|
||||
(img
|
||||
? '<img src="' + escapeHTML(img) + '" alt="">'
|
||||
: '') +
|
||||
'<div class="badge-title">' +
|
||||
escapeHTML(titleOf(item)) +
|
||||
'</div>' +
|
||||
@ -1201,30 +1143,28 @@
|
||||
}
|
||||
function renderCollectors(item) {
|
||||
var custom = collectorAvatarsOf(item);
|
||||
var avatars = custom.length
|
||||
? custom
|
||||
: [
|
||||
ASSET + 'collector-1.png',
|
||||
ASSET + 'collector-2.png',
|
||||
ASSET + 'collector-3.png',
|
||||
ASSET + 'collector-4.png',
|
||||
ASSET + 'collector-5.png',
|
||||
];
|
||||
byId('collectorList').innerHTML =
|
||||
avatars
|
||||
custom
|
||||
.slice(0, 5)
|
||||
.map(function (src) {
|
||||
return (
|
||||
'<img src="' + escapeHTML(src) + '" alt="">'
|
||||
);
|
||||
})
|
||||
.join('') + '<div class="collector-more">...</div>';
|
||||
.join('') +
|
||||
(custom.length
|
||||
? '<div class="collector-more">...</div>'
|
||||
: '');
|
||||
}
|
||||
function openModal(item) {
|
||||
state.selected = item;
|
||||
var unlocked = isUnlocked(item);
|
||||
var resource = resourceOf(item);
|
||||
byId('modalBadge').src = resourceImage(resource, unlocked);
|
||||
setImage(
|
||||
byId('modalBadge'),
|
||||
resourceImage(resource, unlocked),
|
||||
''
|
||||
);
|
||||
byId('modalBadge').classList.toggle('locked', !unlocked);
|
||||
byId('modalTitle').textContent = titleOf(item);
|
||||
byId('modalDesc').textContent = descriptionOf(item);
|
||||
@ -1295,6 +1235,7 @@
|
||||
byId('badgeGrid').addEventListener('click', function (event) {
|
||||
var target = event.target.closest('[data-index]');
|
||||
if (!target) return;
|
||||
target.blur();
|
||||
var list = state.achievements[state.tab] || [];
|
||||
var item = list[Number(target.dataset.index)];
|
||||
if (item) openModal(item);
|
||||
@ -1314,7 +1255,6 @@
|
||||
|
||||
render();
|
||||
window.HyAppBridge.ready({ page: 'achievement' });
|
||||
if (applyLocalPreview()) return;
|
||||
loadTab(state.tab).finally(function () {
|
||||
loadUserProfile();
|
||||
loadCounts();
|
||||
|
||||
BIN
common/png/loading-cat.webp
Normal file
BIN
common/png/loading-cat.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 643 KiB |
@ -103,3 +103,64 @@
|
||||
--hy-theme-radius-pill: 0;
|
||||
--hy-theme-radius-circle: 0;
|
||||
}
|
||||
|
||||
.hy-loading {
|
||||
display: none;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 10px;
|
||||
color: var(--hy-theme-muted);
|
||||
text-align: center;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.hy-loading.active,
|
||||
.hy-loading.is-active {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.hy-loading__mascot {
|
||||
width: 72px;
|
||||
height: 72px;
|
||||
background: url('./png/loading-cat.webp') center / contain no-repeat;
|
||||
animation: hy-loading-float 1.6s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.hy-loading__spinner {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
border: 2px solid rgba(125, 87, 199, 0.18);
|
||||
border-top-color: var(--hy-theme-button);
|
||||
border-radius: 50%;
|
||||
animation: hy-loading-spin 0.86s linear infinite;
|
||||
}
|
||||
|
||||
.hy-loading__text {
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
line-height: 16px;
|
||||
}
|
||||
|
||||
@keyframes hy-loading-float {
|
||||
0%,
|
||||
100% {
|
||||
transform: translateY(0);
|
||||
}
|
||||
50% {
|
||||
transform: translateY(-6px);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes hy-loading-spin {
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.hy-loading__mascot,
|
||||
.hy-loading__spinner {
|
||||
animation: none;
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user