merge master into codex/wechat-mini-program-virtual-payment

This commit is contained in:
kdletters
2026-05-30 16:46:11 +08:00
119 changed files with 3345 additions and 428 deletions

View File

@@ -150,6 +150,7 @@ import {
isWoodenFishGalleryEntry,
type PlatformPublicGalleryCard,
type PlatformWorldCardLike,
resolvePlatformWorkAuthorDisplayName,
resolvePlatformPublicWorkCode,
resolvePlatformWorldCoverImage,
resolvePlatformWorldCoverSlides,
@@ -247,7 +248,6 @@ const AVATAR_MAX_FILE_SIZE = 5 * 1024 * 1024;
const AVATAR_OUTPUT_SIZE = 256;
const AVATAR_ALLOWED_TYPES = new Set(['image/jpeg', 'image/png', 'image/webp']);
const PLATFORM_WORK_COVER_CAROUSEL_INTERVAL_MS = 4200;
const PROFILE_INVITE_REDEEM_ENTRY_VISIBLE_MS = 24 * 60 * 60 * 1000;
const PROFILE_INVITE_QUERY_KEYS = ['inviteCode', 'invite_code'] as const;
const RECOMMEND_ENTRY_SWIPE_THRESHOLD_PX = 36;
const RECOMMEND_ENTRY_COMMIT_ANIMATION_MS = 180;
@@ -615,6 +615,7 @@ function WorldCard({
onClick,
className,
authorAvatarUrl,
authorSummary,
feedCardKey,
enableCoverCarousel = false,
isCoverCarouselActive = false,
@@ -624,6 +625,7 @@ function WorldCard({
onClick: () => void;
className?: string;
authorAvatarUrl?: string | null;
authorSummary?: PublicUserSummary | null;
feedCardKey?: string;
enableCoverCarousel?: boolean;
isCoverCarouselActive?: boolean;
@@ -657,7 +659,10 @@ function WorldCard({
const remixCount = getPlatformWorldRemixCount(entry);
const likeCount = getPlatformWorldLikeCount(entry);
const typeLabel = describePublicGalleryCardKind(entry);
const authorName = entry.authorDisplayName.trim() || '玩家';
const authorName = resolvePlatformWorkAuthorDisplayName(
entry,
authorSummary,
);
const authorAvatarLabel = getPublicAuthorAvatarLabel(authorName);
const normalizedAuthorAvatarUrl = authorAvatarUrl?.trim() ?? '';
const cardLabel = `${entry.worldName}${typeLabel}${formatCompactCount(playCount)}游玩,${formatCompactCount(remixCount)}改造,${formatCompactCount(likeCount)}点赞`;
@@ -939,6 +944,7 @@ function RecommendRuntimePreviewCard({
function RecommendSwipeCard({
entry,
authorAvatarUrl,
authorSummary,
isActive,
visual,
shareState,
@@ -952,6 +958,7 @@ function RecommendSwipeCard({
}: {
entry: PlatformPublicGalleryCard;
authorAvatarUrl?: string | null;
authorSummary?: PublicUserSummary | null;
isActive: boolean;
visual: ReactNode;
shareState?: 'idle' | 'copied' | 'failed';
@@ -976,6 +983,7 @@ function RecommendSwipeCard({
<RecommendRuntimeMeta
entry={entry}
authorAvatarUrl={authorAvatarUrl}
authorSummary={authorSummary}
isActive={isActive}
shareState={shareState}
onDragPointerDown={onDragPointerDown}
@@ -994,6 +1002,7 @@ function RecommendSwipeCard({
function RecommendRuntimeMeta({
entry,
authorAvatarUrl,
authorSummary,
onDragPointerDown,
onDragPointerMove,
onDragPointerUp,
@@ -1006,6 +1015,7 @@ function RecommendRuntimeMeta({
}: {
entry: PlatformPublicGalleryCard;
authorAvatarUrl?: string | null;
authorSummary?: PublicUserSummary | null;
onDragPointerDown?: (event: PointerEvent<HTMLElement>) => void;
onDragPointerMove?: (event: PointerEvent<HTMLElement>) => void;
onDragPointerUp?: (event: PointerEvent<HTMLElement>) => void;
@@ -1018,7 +1028,10 @@ function RecommendRuntimeMeta({
}) {
const likeCount = getPlatformWorldLikeCount(entry);
const remixCount = getPlatformWorldRemixCount(entry);
const authorName = entry.authorDisplayName.trim() || '玩家';
const authorName = resolvePlatformWorkAuthorDisplayName(
entry,
authorSummary,
);
const authorAvatarLabel = getPublicAuthorAvatarLabel(authorName);
const normalizedAuthorAvatarUrl = authorAvatarUrl?.trim() ?? '';
const displayName = formatPlatformWorkDisplayName(entry.worldName);
@@ -1894,28 +1907,28 @@ async function getPublicWorkAuthorSummary(
function describePublicGalleryCardKind(entry: PlatformPublicGalleryCard) {
if (isBigFishGalleryEntry(entry)) {
return formatPlatformWorkDisplayTag('??');
return formatPlatformWorkDisplayTag('大鱼吃小鱼');
}
if (isPuzzleGalleryEntry(entry)) {
return formatPlatformWorkDisplayTag('??');
return formatPlatformWorkDisplayTag('拼图');
}
if (isMatch3DGalleryEntry(entry)) {
return formatPlatformWorkDisplayTag('??');
return formatPlatformWorkDisplayTag('抓大鹅');
}
if (isSquareHoleGalleryEntry(entry)) {
return formatPlatformWorkDisplayTag('??');
return formatPlatformWorkDisplayTag('方洞挑战');
}
if (isJumpHopGalleryEntry(entry)) {
return formatPlatformWorkDisplayTag('???');
return formatPlatformWorkDisplayTag('跳一跳');
}
if (isWoodenFishGalleryEntry(entry)) {
return formatPlatformWorkDisplayTag('???');
return formatPlatformWorkDisplayTag('敲木鱼');
}
if (isVisualNovelGalleryEntry(entry)) {
return formatPlatformWorkDisplayTag('??');
return formatPlatformWorkDisplayTag('视觉小说');
}
if (isBarkBattleGalleryEntry(entry)) {
return formatPlatformWorkDisplayTag('??');
return formatPlatformWorkDisplayTag('汪汪声浪');
}
if (isEdutainmentGalleryEntry(entry)) {
return formatPlatformWorkDisplayTag(entry.templateName);
@@ -2214,21 +2227,6 @@ function formatDashboardCount(value: number) {
return normalizedValue.toLocaleString('zh-CN');
}
function isWithinProfileInviteRedeemWindow(
createdAt: string | null | undefined,
) {
if (!createdAt) {
return false;
}
const createdTime = new Date(createdAt).getTime();
if (Number.isNaN(createdTime)) {
return false;
}
return Date.now() - createdTime <= PROFILE_INVITE_REDEEM_ENTRY_VISIBLE_MS;
}
function normalizeProfileInviteQueryCode(value: string | null | undefined) {
return (value ?? '')
.trim()
@@ -2273,16 +2271,13 @@ function buildPublicUserCode(user: AuthUser | null | undefined) {
return user.publicUserCode.trim();
}
const raw =
user?.id.replace(/[^a-zA-Z0-9]/gu, '').toUpperCase() ||
user?.username.replace(/[^a-zA-Z0-9]/gu, '').toUpperCase() ||
'00000000';
const raw = user?.id.replace(/[^a-zA-Z0-9]/gu, '').toUpperCase() || '00000000';
return `SY-${raw.slice(-8).padStart(8, '0')}`;
}
function getUserAvatarLabel(user: AuthUser | null | undefined) {
return (user?.displayName || user?.username || '叙')
return (user?.displayName || '叙')
.slice(0, 1)
.toUpperCase();
}
@@ -4198,6 +4193,17 @@ export function RpgEntryHomeView({
},
[publicAuthorSummariesByKey],
);
const getPublicEntryAuthorSummary = useCallback(
(entry: PlatformPublicGalleryCard) => {
const authorLookupKey = buildPublicWorkAuthorLookupKey(entry);
if (!authorLookupKey) {
return null;
}
return publicAuthorSummariesByKey[authorLookupKey] ?? null;
},
[publicAuthorSummariesByKey],
);
const activeCategoryGroup =
categoryGroups.find((group) => group.tag === selectedCategoryTag) ??
categoryGroups[0] ??
@@ -4930,18 +4936,14 @@ export function RpgEntryHomeView({
});
}, []);
useEffect(() => {
if (
activeTab !== 'profile' ||
!isAuthenticated ||
!isWithinProfileInviteRedeemWindow(authUi?.user?.createdAt)
) {
if (activeTab !== 'profile' || !isAuthenticated) {
setIsReferralCenterInitialized(false);
setReferralCenter(null);
return;
}
loadReferralCenter();
}, [activeTab, authUi?.user?.createdAt, isAuthenticated, loadReferralCenter]);
}, [activeTab, isAuthenticated, loadReferralCenter]);
const openProfilePopupPanel = (panel: ProfileReferralPanel) => {
setProfilePopupPanel(panel);
setReferralError(null);
@@ -5589,6 +5591,9 @@ export function RpgEntryHomeView({
authorAvatarUrl={getPublicEntryAuthorAvatarUrl(
previousRecommendEntry,
)}
authorSummary={getPublicEntryAuthorSummary(
previousRecommendEntry,
)}
isActive={false}
visual={
<RecommendRuntimePreviewCard
@@ -5606,6 +5611,9 @@ export function RpgEntryHomeView({
authorAvatarUrl={getPublicEntryAuthorAvatarUrl(
activeRecommendEntry,
)}
authorSummary={getPublicEntryAuthorSummary(
activeRecommendEntry,
)}
isActive
visual={
<div className="platform-recommend-runtime-viewport">
@@ -5630,6 +5638,9 @@ export function RpgEntryHomeView({
authorAvatarUrl={getPublicEntryAuthorAvatarUrl(
nextRecommendEntry,
)}
authorSummary={getPublicEntryAuthorSummary(
nextRecommendEntry,
)}
isActive={false}
visual={
<RecommendRuntimePreviewCard
@@ -5783,6 +5794,7 @@ export function RpgEntryHomeView({
onClick={() => onOpenGalleryDetail(entry)}
className="w-full"
authorAvatarUrl={getPublicEntryAuthorAvatarUrl(entry)}
authorSummary={getPublicEntryAuthorSummary(entry)}
feedCardKey={cardKey}
/>
);
@@ -5844,13 +5856,14 @@ export function RpgEntryHomeView({
return (
<WorldCard
key={`${cardKey}:mobile-feed:${discoverChannel}`}
entry={entry}
onClick={() => onOpenGalleryDetail(entry)}
className="w-full"
authorAvatarUrl={getPublicEntryAuthorAvatarUrl(entry)}
feedCardKey={cardKey}
enableCoverCarousel={mobileFeedCarouselEnabled}
isCoverCarouselActive={
entry={entry}
onClick={() => onOpenGalleryDetail(entry)}
className="w-full"
authorAvatarUrl={getPublicEntryAuthorAvatarUrl(entry)}
authorSummary={getPublicEntryAuthorSummary(entry)}
feedCardKey={cardKey}
enableCoverCarousel={mobileFeedCarouselEnabled}
isCoverCarouselActive={
mobileCenteredCardKey === cardKey
}
/>
@@ -5954,6 +5967,7 @@ export function RpgEntryHomeView({
onClick={() => openRecommendGalleryDetail(entry)}
className="w-full min-w-0"
authorAvatarUrl={getPublicEntryAuthorAvatarUrl(entry)}
authorSummary={getPublicEntryAuthorSummary(entry)}
/>
))}
</div>
@@ -5981,6 +5995,7 @@ export function RpgEntryHomeView({
onClick={() => openRecommendGalleryDetail(entry)}
className="w-full min-w-0"
authorAvatarUrl={getPublicEntryAuthorAvatarUrl(entry)}
authorSummary={getPublicEntryAuthorSummary(entry)}
/>
))}
{onOpenChildMotionDemo ? (
@@ -6041,6 +6056,7 @@ export function RpgEntryHomeView({
onClick={() => openRecommendGalleryDetail(entry)}
className="w-full min-w-0"
authorAvatarUrl={getPublicEntryAuthorAvatarUrl(entry)}
authorSummary={getPublicEntryAuthorSummary(entry)}
/>
))}
</div>
@@ -6556,6 +6572,7 @@ export function RpgEntryHomeView({
onClick={() => openRecommendGalleryDetail(entry)}
className="w-full min-w-0"
authorAvatarUrl={getPublicEntryAuthorAvatarUrl(entry)}
authorSummary={getPublicEntryAuthorSummary(entry)}
/>
))}
</div>
@@ -6726,6 +6743,7 @@ export function RpgEntryHomeView({
onClick={() => openRecommendGalleryDetail(entry)}
className="w-full min-w-0"
authorAvatarUrl={getPublicEntryAuthorAvatarUrl(entry)}
authorSummary={getPublicEntryAuthorSummary(entry)}
/>
))}
</div>