Merge branch 'master' into codex/puzzle-clear-template-runtime-fixes
This commit is contained in:
@@ -14,9 +14,9 @@ import {
|
||||
Gamepad2,
|
||||
GitFork,
|
||||
Heart,
|
||||
Loader2,
|
||||
LogIn,
|
||||
MessageCircle,
|
||||
Loader2,
|
||||
Palette,
|
||||
Pencil,
|
||||
Plus,
|
||||
@@ -24,7 +24,6 @@ import {
|
||||
Search,
|
||||
Settings,
|
||||
Share2,
|
||||
ShieldCheck,
|
||||
SlidersHorizontal,
|
||||
Sparkles,
|
||||
Star,
|
||||
@@ -39,6 +38,7 @@ import {
|
||||
type CSSProperties,
|
||||
type PointerEvent,
|
||||
type ReactNode,
|
||||
Suspense,
|
||||
useCallback,
|
||||
useEffect,
|
||||
useMemo,
|
||||
@@ -80,6 +80,7 @@ import type {
|
||||
} from '../../../packages/shared/src/contracts/runtime';
|
||||
import type { HydratedSavedGameSnapshot } from '../../persistence/runtimeSnapshotTypes';
|
||||
import { buildPublicWorkDetailUrl } from '../../routing/appPageRoutes';
|
||||
import { refreshStoredAccessToken } from '../../services/apiClient';
|
||||
import type { AuthUser } from '../../services/authService';
|
||||
import {
|
||||
getPublicAuthUserByCode,
|
||||
@@ -136,6 +137,7 @@ import { getInitialPlatformDesktopLayout } from '../platform-entry/platformEntry
|
||||
import { ResolvedAssetImage } from '../ResolvedAssetImage';
|
||||
import { RpgEntryBrandLogo } from './RpgEntryBrandLogo';
|
||||
import {
|
||||
buildPlatformPublicGalleryCardKey,
|
||||
buildPlatformWorldDisplayTags,
|
||||
describePlatformThemeLabel,
|
||||
formatPlatformWorkDisplayName,
|
||||
@@ -153,8 +155,8 @@ import {
|
||||
isWoodenFishGalleryEntry,
|
||||
type PlatformPublicGalleryCard,
|
||||
type PlatformWorldCardLike,
|
||||
resolvePlatformWorkAuthorDisplayName,
|
||||
resolvePlatformPublicWorkCode,
|
||||
resolvePlatformWorkAuthorDisplayName,
|
||||
resolvePlatformWorldCoverImage,
|
||||
resolvePlatformWorldCoverSlides,
|
||||
resolvePlatformWorldFallbackCoverImage,
|
||||
@@ -196,6 +198,7 @@ export interface RpgEntryHomeViewProps {
|
||||
recommendRuntimeContent?: ReactNode;
|
||||
activeRecommendEntryKey?: string | null;
|
||||
isStartingRecommendEntry?: boolean;
|
||||
isRecommendRuntimeReady?: boolean;
|
||||
recommendRuntimeError?: string | null;
|
||||
onSelectNextRecommendEntry?: (activeEntryKey?: string | null) => void;
|
||||
onSelectPreviousRecommendEntry?: (activeEntryKey?: string | null) => void;
|
||||
@@ -247,6 +250,9 @@ const PLATFORM_HOME_TABS: PlatformHomeTab[] = [
|
||||
'saves',
|
||||
'profile',
|
||||
];
|
||||
const PROFILE_TASK_DAY_MS = 24 * 60 * 60 * 1000;
|
||||
const PROFILE_TASK_BEIJING_OFFSET_MS = 8 * 60 * 60 * 1000;
|
||||
const PROFILE_TASK_MIN_RESET_DELAY_MS = 1000;
|
||||
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']);
|
||||
@@ -302,15 +308,8 @@ function buildProfileTaskCardSummary(center: ProfileTaskCenterResponse | null) {
|
||||
const progressCount = Math.min(task?.progressCount ?? 0, threshold);
|
||||
const rewardPoints =
|
||||
task?.rewardPoints ?? PROFILE_TASK_CARD_FALLBACK_REWARD_POINTS;
|
||||
const actionLabel =
|
||||
task?.status === 'claimable'
|
||||
? '领取'
|
||||
: task?.status === 'claimed'
|
||||
? '已完成'
|
||||
: '去完成';
|
||||
|
||||
return {
|
||||
actionLabel,
|
||||
progressCount,
|
||||
progressPercent: Math.round((progressCount / threshold) * 100),
|
||||
rewardPoints,
|
||||
@@ -318,6 +317,15 @@ function buildProfileTaskCardSummary(center: ProfileTaskCenterResponse | null) {
|
||||
};
|
||||
}
|
||||
|
||||
function getDelayUntilNextProfileTaskReset(nowMs = Date.now()) {
|
||||
const shiftedNow = nowMs + PROFILE_TASK_BEIJING_OFFSET_MS;
|
||||
const nextDayStart =
|
||||
Math.floor(shiftedNow / PROFILE_TASK_DAY_MS) * PROFILE_TASK_DAY_MS +
|
||||
PROFILE_TASK_DAY_MS;
|
||||
const nextResetAt = nextDayStart - PROFILE_TASK_BEIJING_OFFSET_MS;
|
||||
return Math.max(PROFILE_TASK_MIN_RESET_DELAY_MS, nextResetAt - nowMs);
|
||||
}
|
||||
|
||||
type ProfileReferralPanel = 'invite' | 'redeem' | 'community';
|
||||
type ProfilePopupPanel = ProfileReferralPanel | 'saveArchives';
|
||||
type BarcodeDetectorLike = {
|
||||
@@ -947,6 +955,115 @@ function RecommendRuntimePreviewCard({
|
||||
);
|
||||
}
|
||||
|
||||
function RecommendRuntimeCover({
|
||||
entry,
|
||||
className = '',
|
||||
}: {
|
||||
entry: PlatformPublicGalleryCard;
|
||||
className?: string;
|
||||
}) {
|
||||
const coverImage = resolvePlatformWorldCoverImage(entry);
|
||||
const fallbackCoverImage = resolvePlatformWorldFallbackCoverImage(entry);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`platform-recommend-runtime-cover ${className}`}
|
||||
aria-hidden="true"
|
||||
>
|
||||
{coverImage || fallbackCoverImage ? (
|
||||
<PlatformWorkCoverArtwork
|
||||
entry={entry}
|
||||
imageSrc={coverImage}
|
||||
fallbackSrc={fallbackCoverImage}
|
||||
alt=""
|
||||
className="absolute inset-0 h-full w-full object-cover"
|
||||
/>
|
||||
) : (
|
||||
<div className="absolute inset-0 bg-[radial-gradient(circle_at_22%_18%,rgba(255,255,255,0.28),transparent_30%),linear-gradient(135deg,rgba(255,118,117,0.42),rgba(89,164,255,0.34))]" />
|
||||
)}
|
||||
<div className="absolute inset-0 bg-[linear-gradient(180deg,rgba(0,0,0,0.05),rgba(0,0,0,0.34))]" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function RecommendRuntimeMountedProbe({
|
||||
onMounted,
|
||||
}: {
|
||||
onMounted: () => void;
|
||||
}) {
|
||||
useEffect(() => {
|
||||
const animationFrameId = window.requestAnimationFrame(onMounted);
|
||||
return () => window.cancelAnimationFrame(animationFrameId);
|
||||
}, [onMounted]);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function RecommendRuntimeVisual({
|
||||
entry,
|
||||
runtimeContent,
|
||||
isStarting,
|
||||
isRuntimeReady,
|
||||
}: {
|
||||
entry: PlatformPublicGalleryCard;
|
||||
runtimeContent?: ReactNode;
|
||||
isStarting: boolean;
|
||||
isRuntimeReady: boolean;
|
||||
}) {
|
||||
const [isRuntimeMounted, setIsRuntimeMounted] = useState(false);
|
||||
const activeEntryKey = buildPublicGalleryCardKey(entry);
|
||||
const previousEntryKeyRef = useRef(activeEntryKey);
|
||||
|
||||
useEffect(() => {
|
||||
if (previousEntryKeyRef.current === activeEntryKey) {
|
||||
return;
|
||||
}
|
||||
previousEntryKeyRef.current = activeEntryKey;
|
||||
setIsRuntimeMounted((currentValue) => {
|
||||
// 中文注释:拼图推荐流“下一关”会在同一个 run 内切到相似作品;
|
||||
// 此时只更新作品信息和分享基准,不应重显封面造成运行态闪跳。
|
||||
if (currentValue && !isStarting && isRuntimeReady) {
|
||||
return currentValue;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}, [activeEntryKey, isRuntimeReady, isStarting]);
|
||||
|
||||
const handleRuntimeMounted = useCallback(() => {
|
||||
if (!isStarting && isRuntimeReady) {
|
||||
setIsRuntimeMounted(true);
|
||||
}
|
||||
}, [isRuntimeReady, isStarting]);
|
||||
|
||||
const shouldShowCover =
|
||||
!runtimeContent || isStarting || !isRuntimeReady || !isRuntimeMounted;
|
||||
|
||||
return (
|
||||
<div className="platform-recommend-runtime-visual">
|
||||
{runtimeContent ? (
|
||||
<Suspense fallback={null}>
|
||||
<div
|
||||
className="platform-recommend-runtime-viewport"
|
||||
aria-hidden={shouldShowCover}
|
||||
>
|
||||
{runtimeContent}
|
||||
</div>
|
||||
<RecommendRuntimeMountedProbe
|
||||
key={activeEntryKey}
|
||||
onMounted={handleRuntimeMounted}
|
||||
/>
|
||||
</Suspense>
|
||||
) : null}
|
||||
<RecommendRuntimeCover
|
||||
entry={entry}
|
||||
className={
|
||||
shouldShowCover ? '' : 'platform-recommend-runtime-cover--hidden'
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function RecommendSwipeCard({
|
||||
entry,
|
||||
authorAvatarUrl,
|
||||
@@ -1802,22 +1919,7 @@ function isExactPublicWorkCodeSearch(
|
||||
}
|
||||
|
||||
function buildPublicGalleryCardKey(entry: PlatformPublicGalleryCard) {
|
||||
const kind = isBigFishGalleryEntry(entry)
|
||||
? 'big-fish'
|
||||
: isPuzzleGalleryEntry(entry)
|
||||
? 'puzzle'
|
||||
: isMatch3DGalleryEntry(entry)
|
||||
? 'match3d'
|
||||
: isSquareHoleGalleryEntry(entry)
|
||||
? 'square-hole'
|
||||
: isVisualNovelGalleryEntry(entry)
|
||||
? 'visual-novel'
|
||||
: isBarkBattleGalleryEntry(entry)
|
||||
? 'bark-battle'
|
||||
: isEdutainmentGalleryEntry(entry)
|
||||
? `edutainment:${entry.templateId}`
|
||||
: 'rpg';
|
||||
return `${kind}:${entry.ownerUserId}:${entry.profileId}`;
|
||||
return buildPlatformPublicGalleryCardKey(entry);
|
||||
}
|
||||
|
||||
function PlatformWorkSearchResults({
|
||||
@@ -2400,7 +2502,7 @@ function ProfileStatCard({
|
||||
type="button"
|
||||
onClick={onClick ? () => onClick(cardKey) : undefined}
|
||||
aria-label={`${label} ${value}`}
|
||||
className="platform-profile-stat-card flex min-h-[5.75rem] items-center justify-center gap-2 px-3 py-3 text-center transition"
|
||||
className="platform-profile-stat-card flex min-h-[5.25rem] items-center justify-center gap-2 px-2.5 py-2.5 text-center transition"
|
||||
>
|
||||
<div className="platform-profile-stat-card__icon">
|
||||
{imageSrc ? (
|
||||
@@ -2410,10 +2512,10 @@ function ProfileStatCard({
|
||||
)}
|
||||
</div>
|
||||
<div className="min-w-0 text-left">
|
||||
<div className="platform-profile-stat-card__value whitespace-nowrap text-lg font-black leading-none text-[var(--platform-text-strong)]">
|
||||
<div className="platform-profile-stat-card__value whitespace-nowrap text-[16px] font-black leading-none text-[var(--platform-text-strong)]">
|
||||
{value}
|
||||
</div>
|
||||
<div className="platform-profile-stat-card__label mt-1 whitespace-nowrap text-[12px] font-medium text-[var(--platform-text-soft)]">
|
||||
<div className="platform-profile-stat-card__label mt-1 whitespace-nowrap text-[11px] font-medium text-[var(--platform-text-soft)]">
|
||||
{label}
|
||||
</div>
|
||||
</div>
|
||||
@@ -2449,7 +2551,7 @@ function ProfileShortcutButton({
|
||||
<button
|
||||
type="button"
|
||||
onClick={onClick ?? undefined}
|
||||
className="platform-profile-shortcut-button flex min-h-[5.25rem] w-full flex-col items-center justify-center gap-2 px-2.5 py-3 text-center transition"
|
||||
className="platform-profile-shortcut-button flex min-h-[4.75rem] w-full flex-col items-center justify-center gap-1.5 px-2 py-2.5 text-center transition"
|
||||
>
|
||||
<div className="platform-profile-shortcut-button__icon">
|
||||
{imageSrc ? (
|
||||
@@ -2458,11 +2560,11 @@ function ProfileShortcutButton({
|
||||
<Icon className="h-[1.125rem] w-[1.125rem]" />
|
||||
)}
|
||||
</div>
|
||||
<div className="platform-profile-shortcut-button__label whitespace-nowrap text-[13px] font-semibold text-[var(--platform-text-strong)]">
|
||||
<div className="platform-profile-shortcut-button__label whitespace-nowrap text-[12px] font-semibold text-[var(--platform-text-strong)]">
|
||||
{label}
|
||||
</div>
|
||||
{subLabel ? (
|
||||
<div className="platform-profile-shortcut-button__sub-label flex min-h-4 items-center justify-center gap-1 whitespace-nowrap text-[11px] font-medium text-[var(--platform-text-soft)]">
|
||||
<div className="platform-profile-shortcut-button__sub-label flex min-h-4 items-center justify-center gap-1 whitespace-nowrap text-[10px] font-medium text-[var(--platform-text-soft)]">
|
||||
{subLabel}
|
||||
</div>
|
||||
) : null}
|
||||
@@ -2485,13 +2587,13 @@ function ProfileSettingsRow({
|
||||
<button
|
||||
type="button"
|
||||
onClick={onClick}
|
||||
className="platform-profile-settings-row flex w-full items-center justify-between gap-3 px-4 py-4 text-left transition"
|
||||
className="platform-profile-settings-row flex w-full items-center justify-between gap-3 px-4 py-3 text-left transition"
|
||||
>
|
||||
<span className="flex min-w-0 items-center gap-3">
|
||||
<span className="platform-profile-settings-row__icon">
|
||||
<Icon className="h-5 w-5" />
|
||||
<Icon className="h-4 w-4" />
|
||||
</span>
|
||||
<span className="truncate text-[15px] font-semibold text-[var(--platform-text-strong)]">
|
||||
<span className="truncate text-[14px] font-semibold text-[var(--platform-text-strong)]">
|
||||
{label}
|
||||
</span>
|
||||
</span>
|
||||
@@ -4027,6 +4129,7 @@ export function RpgEntryHomeView({
|
||||
recommendRuntimeContent,
|
||||
activeRecommendEntryKey = null,
|
||||
isStartingRecommendEntry = false,
|
||||
isRecommendRuntimeReady = false,
|
||||
recommendRuntimeError = null,
|
||||
onSelectNextRecommendEntry,
|
||||
onSelectPreviousRecommendEntry,
|
||||
@@ -5022,6 +5125,40 @@ export function RpgEntryHomeView({
|
||||
loadTaskCenter();
|
||||
}, [activeTab, isAuthenticated, loadTaskCenter, profileTaskRefreshKey]);
|
||||
|
||||
useEffect(() => {
|
||||
if (activeTab !== 'profile' || !isAuthenticated) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
let cancelled = false;
|
||||
let timer: number | null = null;
|
||||
|
||||
const scheduleNextReset = () => {
|
||||
if (cancelled) {
|
||||
return;
|
||||
}
|
||||
timer = window.setTimeout(() => {
|
||||
void refreshStoredAccessToken({ clearOnFailure: false })
|
||||
.catch(() => undefined)
|
||||
.finally(() => {
|
||||
if (cancelled) {
|
||||
return;
|
||||
}
|
||||
loadTaskCenter();
|
||||
scheduleNextReset();
|
||||
});
|
||||
}, getDelayUntilNextProfileTaskReset());
|
||||
};
|
||||
|
||||
scheduleNextReset();
|
||||
return () => {
|
||||
cancelled = true;
|
||||
if (timer !== null) {
|
||||
window.clearTimeout(timer);
|
||||
}
|
||||
};
|
||||
}, [activeTab, isAuthenticated, loadTaskCenter]);
|
||||
|
||||
const openTaskCenterPanel = () => {
|
||||
setIsTaskCenterOpen(true);
|
||||
setTaskClaimSuccess(null);
|
||||
@@ -5691,10 +5828,6 @@ export function RpgEntryHomeView({
|
||||
{recommendRuntimeError}
|
||||
</button>
|
||||
</section>
|
||||
) : isStartingRecommendEntry ? (
|
||||
<section className="platform-recommend-runtime-panel">
|
||||
<div className="platform-recommend-runtime-state">加载中...</div>
|
||||
</section>
|
||||
) : activeRecommendEntry ? (
|
||||
<div
|
||||
ref={recommendCardStageRef}
|
||||
@@ -5736,9 +5869,12 @@ export function RpgEntryHomeView({
|
||||
)}
|
||||
isActive
|
||||
visual={
|
||||
<div className="platform-recommend-runtime-viewport">
|
||||
{recommendRuntimeContent}
|
||||
</div>
|
||||
<RecommendRuntimeVisual
|
||||
entry={activeRecommendEntry}
|
||||
runtimeContent={recommendRuntimeContent}
|
||||
isStarting={isStartingRecommendEntry}
|
||||
isRuntimeReady={isRecommendRuntimeReady}
|
||||
/>
|
||||
}
|
||||
onDragPointerDown={beginRecommendDrag}
|
||||
onDragPointerMove={moveRecommendDrag}
|
||||
@@ -6324,7 +6460,7 @@ export function RpgEntryHomeView({
|
||||
|
||||
<div className="platform-profile-header__text min-w-0">
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="platform-profile-header__name truncate text-[20px] font-black leading-tight text-[var(--platform-text-strong)]">
|
||||
<div className="platform-profile-header__name truncate text-[18px] font-black leading-tight text-[var(--platform-text-strong)]">
|
||||
{authUi.user.displayName}
|
||||
</div>
|
||||
<button
|
||||
@@ -6333,10 +6469,10 @@ export function RpgEntryHomeView({
|
||||
className="platform-profile-edit-button"
|
||||
aria-label="修改昵称"
|
||||
>
|
||||
<Pencil className="h-5 w-5" />
|
||||
<Pencil className="h-3.5 w-3.5" />
|
||||
</button>
|
||||
</div>
|
||||
<div className="platform-profile-header__code mt-3 flex flex-wrap items-center gap-2 text-[13px] text-[var(--platform-text-base)]">
|
||||
<div className="platform-profile-header__code mt-2 flex flex-wrap items-center gap-2 text-[12px] text-[var(--platform-text-base)]">
|
||||
<span>陶泥号: {publicUserCode}</span>
|
||||
<button
|
||||
type="button"
|
||||
@@ -6365,10 +6501,10 @@ export function RpgEntryHomeView({
|
||||
<Crown className="platform-profile-membership-card__crown" />
|
||||
</span>
|
||||
<span className="min-w-0 flex-1">
|
||||
<span className="platform-profile-membership-card__title block text-[18px] font-black leading-tight text-white">
|
||||
<span className="platform-profile-membership-card__title block text-[16px] font-black leading-tight text-white">
|
||||
普通用户
|
||||
</span>
|
||||
<span className="platform-profile-membership-card__subtitle mt-2 block text-[13px] font-medium text-white/92">
|
||||
<span className="platform-profile-membership-card__subtitle mt-1.5 block text-[12px] font-medium text-white/92">
|
||||
升级会员,享专属特权与福利
|
||||
</span>
|
||||
</span>
|
||||
@@ -6397,7 +6533,7 @@ export function RpgEntryHomeView({
|
||||
/>
|
||||
<ProfileStatCard
|
||||
cardKey="playTime"
|
||||
label="累计游戏时长"
|
||||
label="累计游玩"
|
||||
value="暂不可用"
|
||||
icon={Clock3}
|
||||
imageSrc={profileClockImage}
|
||||
@@ -6405,7 +6541,7 @@ export function RpgEntryHomeView({
|
||||
/>
|
||||
<ProfileStatCard
|
||||
cardKey="playedWorks"
|
||||
label="已玩游戏数量"
|
||||
label="已玩游戏"
|
||||
value="暂不可用"
|
||||
icon={BookOpen}
|
||||
imageSrc={profileGamepadImage}
|
||||
@@ -6424,7 +6560,7 @@ export function RpgEntryHomeView({
|
||||
/>
|
||||
<ProfileStatCard
|
||||
cardKey="playTime"
|
||||
label="累计游戏时长"
|
||||
label="累计游玩"
|
||||
value={totalPlayTime}
|
||||
icon={Clock3}
|
||||
imageSrc={profileClockImage}
|
||||
@@ -6432,7 +6568,7 @@ export function RpgEntryHomeView({
|
||||
/>
|
||||
<ProfileStatCard
|
||||
cardKey="playedWorks"
|
||||
label="已玩游戏数量"
|
||||
label="已玩游戏"
|
||||
value={`${formatDashboardCount(playedWorkCount)}个`}
|
||||
icon={BookOpen}
|
||||
imageSrc={profileGamepadImage}
|
||||
@@ -6452,15 +6588,15 @@ export function RpgEntryHomeView({
|
||||
<span className="platform-profile-daily-task-card__title block text-[15px] font-black text-[var(--platform-text-strong)]">
|
||||
每日任务
|
||||
</span>
|
||||
<span className="platform-profile-daily-task-card__desc mt-4 block text-[13px] font-medium text-[var(--platform-text-base)]">
|
||||
<span className="platform-profile-daily-task-card__desc mt-2 block text-[12px] font-medium text-[var(--platform-text-base)]">
|
||||
完成任务可领取{' '}
|
||||
<span className="text-[#c45b2a]">
|
||||
{profileTaskCardSummary.rewardPoints}
|
||||
</span>{' '}
|
||||
泥点
|
||||
</span>
|
||||
<span className="platform-profile-daily-task-card__progress mt-4 flex items-center gap-3">
|
||||
<span className="platform-profile-daily-task-card__progress-value text-[14px] font-semibold text-[#dc3f0e]">
|
||||
<span className="platform-profile-daily-task-card__progress mt-3 flex items-center gap-3">
|
||||
<span className="platform-profile-daily-task-card__progress-value text-[13px] font-semibold text-[#dc3f0e]">
|
||||
{profileTaskCardSummary.progressCount} /{' '}
|
||||
{profileTaskCardSummary.threshold}
|
||||
</span>
|
||||
@@ -6479,9 +6615,6 @@ export function RpgEntryHomeView({
|
||||
alt=""
|
||||
className="platform-profile-daily-task-card__mascot"
|
||||
/>
|
||||
<span className="platform-profile-daily-task-card__action">
|
||||
{profileTaskCardSummary.actionLabel}
|
||||
</span>
|
||||
</button>
|
||||
|
||||
<section
|
||||
@@ -6505,14 +6638,14 @@ export function RpgEntryHomeView({
|
||||
/>
|
||||
<ProfileShortcutButton
|
||||
label="玩家社区"
|
||||
subLabel="交流心得 领取福利"
|
||||
subLabel="交流心得"
|
||||
icon={MessageCircle}
|
||||
imageSrc={profileCommunityImage}
|
||||
onClick={() => openProfilePopupPanel('community')}
|
||||
/>
|
||||
<ProfileShortcutButton
|
||||
label="反馈与建议"
|
||||
subLabel="帮助我们做得更好"
|
||||
subLabel="帮我们优化产品"
|
||||
icon={MessageCircle}
|
||||
imageSrc={profileFeedbackImage}
|
||||
onClick={onOpenFeedback}
|
||||
@@ -6521,16 +6654,6 @@ export function RpgEntryHomeView({
|
||||
</section>
|
||||
|
||||
<section className="platform-profile-settings-panel" aria-label="设置入口">
|
||||
<ProfileSettingsRow
|
||||
label="主题设置"
|
||||
icon={Palette}
|
||||
onClick={() => authUi.openSettingsModal('appearance')}
|
||||
/>
|
||||
<ProfileSettingsRow
|
||||
label="账号与安全"
|
||||
icon={ShieldCheck}
|
||||
onClick={() => authUi.openSettingsModal('account')}
|
||||
/>
|
||||
<ProfileSettingsRow
|
||||
label="通用设置"
|
||||
icon={Settings}
|
||||
|
||||
Reference in New Issue
Block a user