feat: add platform browse history tab updates
Some checks failed
CI / verify (push) Has been cancelled
Some checks failed
CI / verify (push) Has been cancelled
This commit is contained in:
@@ -1,4 +1,3 @@
|
||||
import { type ComponentType, useMemo } from 'react';
|
||||
import {
|
||||
BookOpen,
|
||||
Camera,
|
||||
@@ -13,6 +12,7 @@ import {
|
||||
Ticket,
|
||||
UserPlus,
|
||||
} from 'lucide-react';
|
||||
import { type ComponentType, useMemo } from 'react';
|
||||
|
||||
import type {
|
||||
CustomWorldGalleryCard,
|
||||
@@ -20,6 +20,7 @@ import type {
|
||||
} from '../../../packages/shared/src/contracts/runtime';
|
||||
import type { HydratedSavedGameSnapshot } from '../../persistence/runtimeSnapshotTypes';
|
||||
import type { AuthUser } from '../../services/authService';
|
||||
import type { PlatformBrowseHistoryEntry } from '../../services/platformBrowseHistory';
|
||||
import type { CustomWorldProfile } from '../../types';
|
||||
import { getNineSliceStyle, UI_CHROME } from '../../uiAssets';
|
||||
import { useAuthUi } from '../auth/AuthUiContext';
|
||||
@@ -33,7 +34,7 @@ import {
|
||||
resolvePlatformWorldLeadPortrait,
|
||||
} from './platformWorldPresentation';
|
||||
|
||||
export type PlatformHomeTab = 'home' | 'create' | 'profile';
|
||||
export type PlatformHomeTab = 'home' | 'create' | 'discover' | 'profile';
|
||||
|
||||
function SectionHeader({ title, detail }: { title: string; detail: string }) {
|
||||
return (
|
||||
@@ -312,6 +313,7 @@ export function PlatformHomeView({
|
||||
featuredEntries,
|
||||
latestEntries,
|
||||
myEntries,
|
||||
historyEntries,
|
||||
isLoadingPlatform,
|
||||
platformError,
|
||||
onContinueGame,
|
||||
@@ -327,6 +329,7 @@ export function PlatformHomeView({
|
||||
featuredEntries: CustomWorldGalleryCard[];
|
||||
latestEntries: CustomWorldGalleryCard[];
|
||||
myEntries: CustomWorldLibraryEntry<CustomWorldProfile>[];
|
||||
historyEntries: PlatformBrowseHistoryEntry[];
|
||||
isLoadingPlatform: boolean;
|
||||
platformError: string | null;
|
||||
onContinueGame: () => void;
|
||||
@@ -365,6 +368,8 @@ export function PlatformHomeView({
|
||||
const tabIcons = {
|
||||
home: "/Icons/Admurin's Pixel Items/Admurin's Pixel Items/Miscellaneous/Singles/192_RustyTrinket_House.png",
|
||||
create: '/Icons/01_Scroll.png',
|
||||
discover:
|
||||
"/Icons/Admurin's Pixel Items/Admurin's Pixel Items/Miscellaneous/Singles/321_Compass.png",
|
||||
profile: '/UI/Icon_Eq_Head.png',
|
||||
} as const;
|
||||
const recentPlayItems = savedSnapshot
|
||||
@@ -519,6 +524,49 @@ export function PlatformHomeView({
|
||||
);
|
||||
}
|
||||
|
||||
if (activeTab === 'discover') {
|
||||
content = (
|
||||
<div className="space-y-4 pb-2">
|
||||
<section
|
||||
className="pixel-nine-slice"
|
||||
style={getNineSliceStyle(UI_CHROME.panel, {
|
||||
paddingX: 18,
|
||||
paddingY: 16,
|
||||
})}
|
||||
>
|
||||
<div className="rounded-full border border-white/10 bg-black/20 px-3 py-1 text-[10px] tracking-[0.2em] text-zinc-100">
|
||||
DISCOVER
|
||||
</div>
|
||||
<div className="mt-4 text-3xl font-black text-white">发现频道</div>
|
||||
<div className="mt-2 max-w-[28rem] text-sm leading-6 text-zinc-300">
|
||||
这里会放后续的专题策展、内容聚合和更多平台频道。首版先保留一个干净的发现位,方便后续扩展。
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<SectionHeader title="最近上新" detail="先看广场里的新内容" />
|
||||
{isLoadingPlatform ? (
|
||||
<EmptyShelf text="正在读取推荐内容..." />
|
||||
) : latestEntries.length > 0 ? (
|
||||
<div className="flex gap-3 overflow-x-auto pb-1 scrollbar-hide">
|
||||
{latestEntries.map((entry: CustomWorldGalleryCard) => (
|
||||
<WorldCard
|
||||
key={`${entry.ownerUserId}:${entry.profileId}:discover`}
|
||||
entry={entry}
|
||||
badge={formatPlatformWorldTime(entry.publishedAt)}
|
||||
metaLabel={describePlatformThemeLabel(entry.themeMode)}
|
||||
onClick={() => onOpenGalleryDetail(entry)}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<EmptyShelf text="发现频道暂时还没有可展示的内容。" />
|
||||
)}
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (activeTab === 'profile') {
|
||||
content = (
|
||||
<div className="space-y-4 pb-2">
|
||||
@@ -662,6 +710,77 @@ export function PlatformHomeView({
|
||||
)}
|
||||
</section>
|
||||
|
||||
<section
|
||||
className="pixel-nine-slice"
|
||||
style={getNineSliceStyle(UI_CHROME.panel, {
|
||||
paddingX: 16,
|
||||
paddingY: 14,
|
||||
})}
|
||||
>
|
||||
<SectionHeader title="历史浏览" detail="最近看过的作品" />
|
||||
{historyEntries.length > 0 ? (
|
||||
<div className="flex gap-3 overflow-x-auto pb-1 scrollbar-hide">
|
||||
{historyEntries.map((entry) => (
|
||||
<button
|
||||
key={`${entry.ownerUserId}:${entry.profileId}:history`}
|
||||
type="button"
|
||||
onClick={() =>
|
||||
onOpenGalleryDetail({
|
||||
ownerUserId: entry.ownerUserId,
|
||||
profileId: entry.profileId,
|
||||
visibility: 'published',
|
||||
publishedAt: entry.visitedAt,
|
||||
updatedAt: entry.visitedAt,
|
||||
worldName: entry.worldName,
|
||||
subtitle: entry.subtitle,
|
||||
summaryText: entry.summaryText,
|
||||
coverImageSrc: entry.coverImageSrc,
|
||||
themeMode: entry.themeMode,
|
||||
authorDisplayName: entry.authorDisplayName,
|
||||
playableNpcCount: 0,
|
||||
landmarkCount: 0,
|
||||
})
|
||||
}
|
||||
className="relative flex h-[10.5rem] w-[17rem] shrink-0 overflow-hidden rounded-[1.4rem] border border-white/10 bg-[linear-gradient(135deg,rgba(25,32,46,0.95),rgba(9,12,18,0.96))] p-4 text-left"
|
||||
>
|
||||
{entry.coverImageSrc ? (
|
||||
<img
|
||||
src={entry.coverImageSrc}
|
||||
alt={entry.worldName}
|
||||
className="absolute inset-0 h-full w-full object-cover opacity-22"
|
||||
style={{ imageRendering: 'pixelated' }}
|
||||
/>
|
||||
) : null}
|
||||
<div className="absolute inset-0 bg-[linear-gradient(180deg,rgba(8,10,14,0.08),rgba(8,10,14,0.9))]" />
|
||||
<div className="relative z-10 flex h-full flex-col">
|
||||
<div className="flex items-start justify-between gap-3">
|
||||
<span className="rounded-full border border-amber-300/20 bg-amber-500/10 px-3 py-1 text-[10px] tracking-[0.2em] text-amber-100">
|
||||
HISTORY
|
||||
</span>
|
||||
<span className="text-[11px] text-zinc-400">
|
||||
{formatSnapshotTime(entry.visitedAt)}
|
||||
</span>
|
||||
</div>
|
||||
<div className="mt-auto">
|
||||
<div className="line-clamp-1 text-xl font-black text-white">
|
||||
{entry.worldName}
|
||||
</div>
|
||||
<div className="mt-1 text-sm text-zinc-300">
|
||||
作者:{entry.authorDisplayName}
|
||||
</div>
|
||||
<div className="mt-2 line-clamp-3 text-xs leading-5 text-zinc-400">
|
||||
{entry.summaryText || entry.subtitle || '等待补充世界摘要。'}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<EmptyShelf text="你最近还没有浏览过作品详情,去首页或发现逛一逛吧。" />
|
||||
)}
|
||||
</section>
|
||||
|
||||
<section
|
||||
className="pixel-nine-slice"
|
||||
style={getNineSliceStyle(UI_CHROME.panel, {
|
||||
@@ -726,7 +845,7 @@ export function PlatformHomeView({
|
||||
className="mt-4 border-t border-white/5 pt-3"
|
||||
style={{ paddingBottom: 'calc(env(safe-area-inset-bottom) + 0.2rem)' }}
|
||||
>
|
||||
<div className="grid h-14 grid-cols-3 gap-1 rounded-[1.2rem] bg-black/18 px-1 py-1">
|
||||
<div className="grid h-14 grid-cols-4 gap-1 rounded-[1.2rem] bg-black/18 px-1 py-1">
|
||||
<PlatformTabButton
|
||||
active={activeTab === 'home'}
|
||||
label="首页"
|
||||
@@ -739,6 +858,12 @@ export function PlatformHomeView({
|
||||
iconSrc={tabIcons.create}
|
||||
onClick={() => onTabChange('create')}
|
||||
/>
|
||||
<PlatformTabButton
|
||||
active={activeTab === 'discover'}
|
||||
label="发现"
|
||||
iconSrc={tabIcons.discover}
|
||||
onClick={() => onTabChange('discover')}
|
||||
/>
|
||||
<PlatformTabButton
|
||||
active={activeTab === 'profile'}
|
||||
label="我的"
|
||||
|
||||
Reference in New Issue
Block a user