调整创作主页与项目入口
新增陶泥儿创作主页和用户素材瀑布流 新增项目入口并复用画布中心封面 保留创作主页左侧导航和上方栏 修复玩家社区在创作页原地弹窗 更新路由、测试和项目记忆文档
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { Image as ImageIcon, Loader2 } from 'lucide-react';
|
||||
import { Loader2 } from 'lucide-react';
|
||||
|
||||
import { AnimatePresence, motion } from 'motion/react';
|
||||
import {
|
||||
@@ -534,6 +534,7 @@ import {
|
||||
PlatformEntryHomeView,
|
||||
type PlatformHomeTab,
|
||||
} from './PlatformEntryHomeView';
|
||||
import { PlatformProfileReferralModal } from './PlatformProfileReferralModal';
|
||||
import { usePlatformDesktopLayout } from './platformEntryResponsive';
|
||||
import {
|
||||
buildCreationHubFallbackItems,
|
||||
@@ -1285,6 +1286,13 @@ const ProjectGalleryView = lazy(async () => {
|
||||
};
|
||||
});
|
||||
|
||||
const CreationLandingView = lazy(async () => {
|
||||
const module = await import('../creation-home/CreationLandingView');
|
||||
return {
|
||||
default: module.CreationLandingView,
|
||||
};
|
||||
});
|
||||
|
||||
const UnifiedCreationWorkspace = lazy(async () => {
|
||||
const module = await import('../unified-creation/UnifiedCreationWorkspace');
|
||||
return {
|
||||
@@ -1620,6 +1628,8 @@ export function PlatformEntryFlowShellImpl({
|
||||
: 'platform-theme--light';
|
||||
const isDesktopLayout = usePlatformDesktopLayout();
|
||||
const [showCreationTypeModal, setShowCreationTypeModal] = useState(false);
|
||||
const [isCreationCommunityOpen, setIsCreationCommunityOpen] =
|
||||
useState(false);
|
||||
const [draftGenerationPointNotice, setDraftGenerationPointNotice] =
|
||||
useState<DraftGenerationPointNotice | null>(null);
|
||||
const [selectedDetailEntry, setSelectedDetailEntry] =
|
||||
@@ -3597,6 +3607,24 @@ export function PlatformEntryFlowShellImpl({
|
||||
]);
|
||||
|
||||
const openCreationTypePicker = useCallback(() => {
|
||||
setSelectionStage('creation-home');
|
||||
}, [setSelectionStage]);
|
||||
|
||||
const openProjectGallery = useCallback(() => {
|
||||
setSelectionStage('project');
|
||||
}, [setSelectionStage]);
|
||||
|
||||
const openCreationCommunity = useCallback(() => {
|
||||
setIsCreationCommunityOpen(true);
|
||||
}, []);
|
||||
|
||||
const openEditorProject = useCallback((projectId: string) => {
|
||||
setSelectionStage('image-editor', {
|
||||
path: `/editor/canvas?projectid=${encodeURIComponent(projectId)}`,
|
||||
});
|
||||
}, [setSelectionStage]);
|
||||
|
||||
const openCreationTypeModal = useCallback(() => {
|
||||
if (!prepareCreationLaunch()) {
|
||||
return;
|
||||
}
|
||||
@@ -15129,29 +15157,164 @@ export function PlatformEntryFlowShellImpl({
|
||||
) : null}
|
||||
</Suspense>
|
||||
);
|
||||
const creationStartContent = (
|
||||
<div className="image-editor-creation-entry-stack">
|
||||
<button
|
||||
type="button"
|
||||
className="image-editor-creation-entry"
|
||||
onClick={() => setSelectionStage('image-editor')}
|
||||
aria-label="打开图片编辑器"
|
||||
>
|
||||
<span className="image-editor-creation-entry__icon">
|
||||
<ImageIcon className="h-5 w-5" />
|
||||
</span>
|
||||
<span className="image-editor-creation-entry__body">
|
||||
<span>图片编辑器</span>
|
||||
<span>画布工具</span>
|
||||
</span>
|
||||
</button>
|
||||
{renderCreationHubContent('start-only', '正在加载创作大厅...')}
|
||||
</div>
|
||||
const creationStartContent = renderCreationHubContent(
|
||||
'start-only',
|
||||
'正在加载创作大厅...',
|
||||
);
|
||||
const draftHubContent = renderCreationHubContent(
|
||||
'works-only',
|
||||
'正在加载草稿列表...',
|
||||
);
|
||||
const creationLandingContent = (
|
||||
<Suspense fallback={<LazyPanelFallback label="正在加载创作主页..." />}>
|
||||
<CreationLandingView
|
||||
onOpenProject={openEditorProject}
|
||||
onOpenProjects={openProjectGallery}
|
||||
onOpenCommunity={openCreationCommunity}
|
||||
publicGalleryEntries={[
|
||||
...featuredGalleryEntries,
|
||||
...latestGalleryEntries,
|
||||
]}
|
||||
/>
|
||||
</Suspense>
|
||||
);
|
||||
const handlePlatformHomeTabChange = useCallback(
|
||||
(tab: PlatformHomeTab) => {
|
||||
platformBootstrap.setPlatformTab(tab);
|
||||
if (selectionStage === 'creation-home' && tab !== 'create') {
|
||||
setSelectionStage('platform');
|
||||
pushAppHistoryPath('/');
|
||||
}
|
||||
},
|
||||
[platformBootstrap, selectionStage, setSelectionStage],
|
||||
);
|
||||
const platformHomeView = (
|
||||
<PlatformEntryHomeView
|
||||
activeTab={
|
||||
selectionStage === 'creation-home'
|
||||
? 'create'
|
||||
: platformBootstrap.platformTab
|
||||
}
|
||||
onTabChange={handlePlatformHomeTabChange}
|
||||
hasSavedGame={hasSavedGame}
|
||||
savedSnapshot={savedSnapshot}
|
||||
saveEntries={platformBootstrap.saveEntries}
|
||||
saveError={platformBootstrap.saveError}
|
||||
featuredEntries={featuredGalleryEntries}
|
||||
latestEntries={latestGalleryEntries}
|
||||
myEntries={platformBootstrap.savedCustomWorldEntries}
|
||||
historyEntries={platformBootstrap.historyEntries}
|
||||
profileDashboard={platformBootstrap.profileDashboard}
|
||||
isLoadingPlatform={platformBootstrap.isLoadingPlatform}
|
||||
isLoadingDashboard={platformBootstrap.isLoadingDashboard}
|
||||
hasUnreadDraftUpdate={hasUnreadDraftUpdates}
|
||||
profileTaskRefreshKey={profileTaskRefreshKey}
|
||||
profileGenerationQueueStatus={profileExternalGenerationQueueStatus}
|
||||
isDesktopLayout={isDesktopLayout}
|
||||
isResumingSaveWorldKey={platformBootstrap.isResumingSaveWorldKey}
|
||||
platformError={
|
||||
platformBootstrap.isLoadingPlatform
|
||||
? null
|
||||
: (platformBootstrapErrorForDisplay ??
|
||||
sessionController.agentWorkspaceRestoreError)
|
||||
}
|
||||
dashboardError={
|
||||
platformBootstrap.isLoadingDashboard
|
||||
? null
|
||||
: platformBootstrap.dashboardError
|
||||
}
|
||||
createTabContent={
|
||||
selectionStage === 'creation-home'
|
||||
? creationLandingContent
|
||||
: creationStartContent
|
||||
}
|
||||
draftTabContent={draftHubContent}
|
||||
onContinueGame={handleContinueGame}
|
||||
onResumeSave={(entry) => {
|
||||
if (
|
||||
(entry.worldType ?? '').toLowerCase() === 'puzzle' ||
|
||||
entry.worldKey.startsWith('puzzle:')
|
||||
) {
|
||||
void resumePuzzleSaveArchive(entry);
|
||||
return;
|
||||
}
|
||||
void platformBootstrap.handleResumeSaveEntry(entry);
|
||||
}}
|
||||
onOpenCreateWorld={openCreationTypeModal}
|
||||
onOpenCreateTypePicker={openCreationTypePicker}
|
||||
onOpenGalleryDetail={openPublicGalleryDetail}
|
||||
onOpenChildMotionDemo={startChildMotionDemo}
|
||||
onOpenBabyLoveDrawing={startBabyLoveDrawingRuntime}
|
||||
onOpenRecommendGalleryDetail={openRecommendGalleryDetail}
|
||||
recommendRuntimeContent={recommendRuntimeContent}
|
||||
activeRecommendEntryKey={activeRecommendEntryKey}
|
||||
isRecommendRuntimeReady={isActiveRecommendRuntimeReady}
|
||||
isStartingRecommendEntry={
|
||||
isStartingRecommendEntry ||
|
||||
isBigFishBusy ||
|
||||
(isPuzzleBusy &&
|
||||
!(activeRecommendRuntimeKind === 'puzzle' && puzzleRun)) ||
|
||||
(isPuzzleClearBusy &&
|
||||
!(
|
||||
activeRecommendRuntimeKind === 'puzzle-clear' &&
|
||||
puzzleClearRun
|
||||
)) ||
|
||||
isMatch3DBusy ||
|
||||
isSquareHoleBusy ||
|
||||
isVisualNovelBusy ||
|
||||
isWoodenFishBusy
|
||||
}
|
||||
recommendRuntimeError={activeRecommendRuntimeError}
|
||||
onSelectNextRecommendEntry={(activeEntryKey) =>
|
||||
selectAdjacentRecommendRuntimeEntry(1, activeEntryKey)
|
||||
}
|
||||
onSelectPreviousRecommendEntry={(activeEntryKey) =>
|
||||
selectAdjacentRecommendRuntimeEntry(-1, activeEntryKey)
|
||||
}
|
||||
onLikeRecommendEntry={(entry) => {
|
||||
likePublicWork(entry);
|
||||
}}
|
||||
onShareRecommendEntry={(entry) => {
|
||||
openRecommendShareModal(entry);
|
||||
}}
|
||||
onRemixRecommendEntry={(entry) => {
|
||||
remixPublicWork(entry);
|
||||
}}
|
||||
onOpenLibraryDetail={(entry) => {
|
||||
runProtectedAction(() => {
|
||||
void detailNavigation.openLibraryDetail(entry);
|
||||
});
|
||||
}}
|
||||
onDeleteLibraryEntry={(entry) => {
|
||||
handleDeleteLibraryEntry(entry);
|
||||
}}
|
||||
deletingLibraryEntryId={deletingCreationWorkId}
|
||||
onSearchPublicCode={(keyword) => {
|
||||
void handlePublicCodeSearch(keyword);
|
||||
}}
|
||||
isSearchingPublicCode={isSearchingPublicCode}
|
||||
profilePlayStats={profilePlayStats}
|
||||
isProfilePlayStatsOpen={isProfilePlayStatsOpen}
|
||||
isProfilePlayStatsLoading={isProfilePlayStatsLoading}
|
||||
profilePlayStatsError={profilePlayStatsError}
|
||||
onCloseProfilePlayStats={() => {
|
||||
setIsProfilePlayStatsOpen(false);
|
||||
}}
|
||||
onOpenPlayedWork={openPlayedWork}
|
||||
onOpenFeedback={openProfileFeedback}
|
||||
onOpenProjects={openProjectGallery}
|
||||
onOpenProfileDashboardCard={(cardKey) => {
|
||||
if (cardKey === 'playedWorks') {
|
||||
openProfilePlayedWorks();
|
||||
return;
|
||||
}
|
||||
if (platformBootstrap.dashboardError) {
|
||||
void platformBootstrap.refreshProfileDashboard();
|
||||
}
|
||||
}}
|
||||
onRechargeSuccess={platformBootstrap.refreshProfileDashboard}
|
||||
/>
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -15181,16 +15344,22 @@ export function PlatformEntryFlowShellImpl({
|
||||
>
|
||||
<Suspense fallback={<LazyPanelFallback label="正在加载项目..." />}>
|
||||
<ProjectGalleryView
|
||||
onOpenProject={(projectId) => {
|
||||
setSelectionStage('image-editor');
|
||||
pushAppHistoryPath(
|
||||
`/editor/canvas?projectid=${encodeURIComponent(projectId)}`,
|
||||
);
|
||||
}}
|
||||
onOpenProject={openEditorProject}
|
||||
/>
|
||||
</Suspense>
|
||||
</motion.div>
|
||||
)}
|
||||
{selectionStage === 'creation-home' && (
|
||||
<motion.div
|
||||
key="creation-home"
|
||||
initial={{ opacity: 0, y: 12 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
exit={{ opacity: 0, y: -12 }}
|
||||
className="flex h-full min-h-0 min-w-0 flex-col overflow-hidden"
|
||||
>
|
||||
{platformHomeView}
|
||||
</motion.div>
|
||||
)}
|
||||
{selectionStage === 'platform' && (
|
||||
<motion.div
|
||||
key="platform-home"
|
||||
@@ -15199,123 +15368,7 @@ export function PlatformEntryFlowShellImpl({
|
||||
exit={{ opacity: 0, y: -12 }}
|
||||
className="flex h-full min-h-0 min-w-0 flex-col overflow-hidden"
|
||||
>
|
||||
<PlatformEntryHomeView
|
||||
activeTab={platformBootstrap.platformTab}
|
||||
onTabChange={platformBootstrap.setPlatformTab}
|
||||
hasSavedGame={hasSavedGame}
|
||||
savedSnapshot={savedSnapshot}
|
||||
saveEntries={platformBootstrap.saveEntries}
|
||||
saveError={platformBootstrap.saveError}
|
||||
featuredEntries={featuredGalleryEntries}
|
||||
latestEntries={latestGalleryEntries}
|
||||
myEntries={platformBootstrap.savedCustomWorldEntries}
|
||||
historyEntries={platformBootstrap.historyEntries}
|
||||
profileDashboard={platformBootstrap.profileDashboard}
|
||||
isLoadingPlatform={platformBootstrap.isLoadingPlatform}
|
||||
isLoadingDashboard={platformBootstrap.isLoadingDashboard}
|
||||
hasUnreadDraftUpdate={hasUnreadDraftUpdates}
|
||||
profileTaskRefreshKey={profileTaskRefreshKey}
|
||||
profileGenerationQueueStatus={profileExternalGenerationQueueStatus}
|
||||
isDesktopLayout={isDesktopLayout}
|
||||
isResumingSaveWorldKey={platformBootstrap.isResumingSaveWorldKey}
|
||||
platformError={
|
||||
platformBootstrap.isLoadingPlatform
|
||||
? null
|
||||
: (platformBootstrapErrorForDisplay ??
|
||||
sessionController.agentWorkspaceRestoreError)
|
||||
}
|
||||
dashboardError={
|
||||
platformBootstrap.isLoadingDashboard
|
||||
? null
|
||||
: platformBootstrap.dashboardError
|
||||
}
|
||||
createTabContent={creationStartContent}
|
||||
draftTabContent={draftHubContent}
|
||||
onContinueGame={handleContinueGame}
|
||||
onResumeSave={(entry) => {
|
||||
if (
|
||||
(entry.worldType ?? '').toLowerCase() === 'puzzle' ||
|
||||
entry.worldKey.startsWith('puzzle:')
|
||||
) {
|
||||
void resumePuzzleSaveArchive(entry);
|
||||
return;
|
||||
}
|
||||
void platformBootstrap.handleResumeSaveEntry(entry);
|
||||
}}
|
||||
onOpenCreateWorld={openCreationTypePicker}
|
||||
onOpenCreateTypePicker={openCreationTypePicker}
|
||||
onOpenGalleryDetail={openPublicGalleryDetail}
|
||||
onOpenChildMotionDemo={startChildMotionDemo}
|
||||
onOpenBabyLoveDrawing={startBabyLoveDrawingRuntime}
|
||||
onOpenRecommendGalleryDetail={openRecommendGalleryDetail}
|
||||
recommendRuntimeContent={recommendRuntimeContent}
|
||||
activeRecommendEntryKey={activeRecommendEntryKey}
|
||||
isRecommendRuntimeReady={isActiveRecommendRuntimeReady}
|
||||
isStartingRecommendEntry={
|
||||
isStartingRecommendEntry ||
|
||||
isBigFishBusy ||
|
||||
(isPuzzleBusy &&
|
||||
!(activeRecommendRuntimeKind === 'puzzle' && puzzleRun)) ||
|
||||
(isPuzzleClearBusy &&
|
||||
!(
|
||||
activeRecommendRuntimeKind === 'puzzle-clear' &&
|
||||
puzzleClearRun
|
||||
)) ||
|
||||
isMatch3DBusy ||
|
||||
isSquareHoleBusy ||
|
||||
isVisualNovelBusy ||
|
||||
isWoodenFishBusy
|
||||
}
|
||||
recommendRuntimeError={activeRecommendRuntimeError}
|
||||
onSelectNextRecommendEntry={(activeEntryKey) =>
|
||||
selectAdjacentRecommendRuntimeEntry(1, activeEntryKey)
|
||||
}
|
||||
onSelectPreviousRecommendEntry={(activeEntryKey) =>
|
||||
selectAdjacentRecommendRuntimeEntry(-1, activeEntryKey)
|
||||
}
|
||||
onLikeRecommendEntry={(entry) => {
|
||||
likePublicWork(entry);
|
||||
}}
|
||||
onShareRecommendEntry={(entry) => {
|
||||
openRecommendShareModal(entry);
|
||||
}}
|
||||
onRemixRecommendEntry={(entry) => {
|
||||
remixPublicWork(entry);
|
||||
}}
|
||||
onOpenLibraryDetail={(entry) => {
|
||||
runProtectedAction(() => {
|
||||
void detailNavigation.openLibraryDetail(entry);
|
||||
});
|
||||
}}
|
||||
onDeleteLibraryEntry={(entry) => {
|
||||
handleDeleteLibraryEntry(entry);
|
||||
}}
|
||||
deletingLibraryEntryId={deletingCreationWorkId}
|
||||
onSearchPublicCode={(keyword) => {
|
||||
void handlePublicCodeSearch(keyword);
|
||||
}}
|
||||
isSearchingPublicCode={isSearchingPublicCode}
|
||||
profilePlayStats={profilePlayStats}
|
||||
isProfilePlayStatsOpen={isProfilePlayStatsOpen}
|
||||
isProfilePlayStatsLoading={isProfilePlayStatsLoading}
|
||||
profilePlayStatsError={profilePlayStatsError}
|
||||
onCloseProfilePlayStats={() => {
|
||||
setIsProfilePlayStatsOpen(false);
|
||||
}}
|
||||
onOpenPlayedWork={openPlayedWork}
|
||||
onOpenFeedback={openProfileFeedback}
|
||||
onOpenProjects={() => setSelectionStage('project')}
|
||||
onOpenProfileDashboardCard={(cardKey) => {
|
||||
if (cardKey === 'playedWorks') {
|
||||
openProfilePlayedWorks();
|
||||
return;
|
||||
}
|
||||
if (platformBootstrap.dashboardError) {
|
||||
void platformBootstrap.refreshProfileDashboard();
|
||||
}
|
||||
}}
|
||||
onRechargeSuccess={platformBootstrap.refreshProfileDashboard}
|
||||
/>
|
||||
{platformHomeView}
|
||||
</motion.div>
|
||||
)}
|
||||
|
||||
@@ -17713,6 +17766,22 @@ export function PlatformEntryFlowShellImpl({
|
||||
overlayClassName={`platform-theme ${platformThemeClass} !items-center`}
|
||||
panelClassName="platform-remap-surface rounded-[1.5rem]"
|
||||
/>
|
||||
{isCreationCommunityOpen ? (
|
||||
<PlatformProfileReferralModal
|
||||
panel="community"
|
||||
center={null}
|
||||
isLoading={false}
|
||||
isSubmittingRedeem={false}
|
||||
redeemCode=""
|
||||
copyInviteState="idle"
|
||||
error={null}
|
||||
success={null}
|
||||
onClose={() => setIsCreationCommunityOpen(false)}
|
||||
onCopyInvite={() => undefined}
|
||||
onRedeemCodeChange={() => undefined}
|
||||
onSubmitRedeemCode={() => undefined}
|
||||
/>
|
||||
) : null}
|
||||
<PlatformAcknowledgeStatusDialog
|
||||
open={Boolean(workNotFoundRecoveryDialog)}
|
||||
status="error"
|
||||
|
||||
Reference in New Issue
Block a user