修复邀请

This commit is contained in:
zhx 2026-06-30 20:50:42 +08:00
parent b6d8e6900d
commit 224808a3e3
3 changed files with 138 additions and 8 deletions

View File

@ -7,7 +7,7 @@
content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover"
/>
<title>Invite Friends</title>
<link rel="stylesheet" href="./style.css?v=20260629-bind-invite" />
<link rel="stylesheet" href="./style.css?v=20260630-invite-layout" />
</head>
<body>
<main
@ -135,6 +135,7 @@
class="ornate-button bind-invite-button"
id="bindInviteButton"
type="button"
hidden
data-i18n="invite.linkInviter"
>
Bind User
@ -315,6 +316,6 @@
<script src="../../common/api.js?v=20260630-invite-code"></script>
<script src="../../common/params.js?v=20260611-invite-referrer"></script>
<script src="../../common/toast.js"></script>
<script src="./script.js?v=20260630-invite-code"></script>
<script src="./script.js?v=20260630-bind-window"></script>
</body>
</html>

View File

@ -20,6 +20,7 @@
'ios_url',
'url',
];
var BIND_INVITE_VISIBLE_WINDOW_MS = 48 * 60 * 60 * 1000;
var params = new URLSearchParams(window.location.search);
var state = {
inviteCode: firstParam(INVITE_CODE_KEYS),
@ -27,6 +28,9 @@
inviterAvatar: firstParam(SHARE_AVATAR_KEYS),
inviteLink: '',
currentUser: null,
currentUserCreatedAtMS: 0,
currentUserTimingLoaded: false,
serverTimeMS: 0,
pendingInviteCode: '',
searchedInviter: null,
inviteRewardStatus: null,
@ -58,12 +62,17 @@
return textValue(value).toUpperCase();
}
function readPath(source, path) {
function readRawPath(source, path) {
var value = source;
for (var i = 0; i < path.length; i += 1) {
if (!value || typeof value !== 'object') return '';
value = value[path[i]];
}
return value;
}
function readPath(source, path) {
var value = readRawPath(source, path);
return textValue(value);
}
@ -75,6 +84,25 @@
return '';
}
function normalizeTimestampMS(value) {
if (value === undefined || value === null || value === '') return 0;
if (typeof value === 'string' && !String(value).trim()) return 0;
var number = Number(value);
if (Number.isFinite(number) && number > 0) {
return number < 100000000000 ? number * 1000 : number;
}
var parsed = Date.parse(value);
return Number.isFinite(parsed) && parsed > 0 ? parsed : 0;
}
function firstTimestampMS(source, paths) {
for (var i = 0; i < paths.length; i += 1) {
var value = normalizeTimestampMS(readRawPath(source, paths[i]));
if (value) return value;
}
return 0;
}
function currentUserPayload(data, profile) {
var target = profile || data || {};
return Boolean(
@ -84,6 +112,8 @@
target.displayUserId ||
target.username ||
target.nickname ||
target.created_at_ms !== undefined ||
target.createdAtMs !== undefined ||
target.profile_completed !== undefined ||
target.profileCompleted !== undefined
);
@ -137,10 +167,97 @@
return payload && payload.user ? payload.user : payload;
}
function applyCurrentUserTiming(data, profile) {
var serverTimeMS = firstTimestampMS(data, [
['server_time_ms'],
['serverTimeMS'],
['serverTimeMs'],
['server_time'],
['serverTime'],
]);
var createdAtMS = firstTimestampMS(data, [
['profile', 'created_at_ms'],
['profile', 'createdAtMs'],
['profile', 'created_at'],
['profile', 'createdAt'],
['profile', 'registered_at_ms'],
['profile', 'registeredAtMs'],
['profile', 'registered_at'],
['profile', 'registeredAt'],
['created_at_ms'],
['createdAtMs'],
['created_at'],
['createdAt'],
['registered_at_ms'],
['registeredAtMs'],
['registered_at'],
['registeredAt'],
]);
if (!createdAtMS) {
createdAtMS = firstTimestampMS(profile, [
['created_at_ms'],
['createdAtMs'],
['created_at'],
['createdAt'],
['registered_at_ms'],
['registeredAtMs'],
['registered_at'],
['registeredAt'],
]);
}
if (serverTimeMS) state.serverTimeMS = serverTimeMS;
if (createdAtMS) state.currentUserCreatedAtMS = createdAtMS;
}
function bindInviteReferenceTimeMS() {
return state.serverTimeMS || Date.now();
}
function canShowBindInviteButton() {
var api = window.HyAppAPI || {};
var hasToken = api.getAccessToken && api.getAccessToken();
if (hasToken && !state.currentUserTimingLoaded) return false;
var current = state.currentUser || {};
var createdAtMS =
state.currentUserCreatedAtMS ||
firstTimestampMS(current, [
['created_at_ms'],
['createdAtMs'],
['created_at'],
['createdAt'],
['registered_at_ms'],
['registeredAtMs'],
['registered_at'],
['registeredAt'],
]);
if (!createdAtMS) return true;
// 48 小时窗口必须用服务端返回的当前时间优先判断;接口缺少时间时才退回本机时间,避免用户手动改系统时间影响显隐。
return (
bindInviteReferenceTimeMS() - createdAtMS <=
BIND_INVITE_VISIBLE_WINDOW_MS
);
}
function renderBindInviteVisibility() {
var button = $('#bindInviteButton');
if (!button) return;
var visible = canShowBindInviteButton();
button.hidden = !visible;
button.disabled = !visible;
if (visible || !$('#bindInviteModal') || $('#bindInviteModal').hidden) {
return;
}
closeBindModal();
}
function applyCurrentUser(payload) {
var data = readOverviewPayload(payload || {});
var profile = data.profile || data;
if (currentUserPayload(data, profile)) mergeCurrentUser(profile);
applyCurrentUserTiming(data, profile);
if (currentUserPayload(data, profile)) {
state.currentUserTimingLoaded = true;
mergeCurrentUser(profile);
}
var inviteCode = firstPath(data, [
['invite', 'my_invite_code'],
['invite', 'myInviteCode'],
@ -186,6 +303,7 @@
$('#statEligible').textContent = validInviteCount;
}
renderInviteShare();
renderBindInviteVisibility();
}
function loadCurrentUser() {
@ -826,6 +944,10 @@
}
function openBindModal() {
if (!canShowBindInviteButton()) {
renderBindInviteVisibility();
return;
}
var api = window.HyAppAPI || {};
if (!api.getAccessToken || !api.getAccessToken()) {
toast(t('invite.loginRequired', 'Please log in first'));
@ -1089,6 +1211,7 @@
renderRank();
updateCountdown();
setInterval(updateCountdown, 1000);
renderBindInviteVisibility();
bindEvents();
renderInviteActivityReward(null);
Promise.all([

View File

@ -318,16 +318,20 @@ p {
.invite-field {
position: relative;
width: 100%;
min-width: 0;
height: calc(93 * var(--u));
overflow: hidden;
background: url('./assets/link-input.svg') center / 100% 100% no-repeat;
}
.link-box {
display: flex;
align-items: center;
justify-content: flex-start;
display: block;
width: 100%;
min-width: 0;
max-width: 100%;
height: 100%;
line-height: calc(93 * var(--u));
padding: 0 calc(112 * var(--u)) 0 calc(52 * var(--u));
overflow: hidden;
color: var(--gold);
@ -335,11 +339,13 @@ p {
font-weight: 700;
text-overflow: ellipsis;
white-space: nowrap;
direction: ltr;
unicode-bidi: plaintext;
}
.invite-field.is-code .link-box {
justify-content: center;
padding-left: calc(112 * var(--u));
text-align: center;
}
.invite-code {