fix: add logged-out login entry
This commit is contained in:
@@ -150,6 +150,8 @@
|
|||||||
- 不再提供 `AuthGate` 层右上角固定悬浮的全局登录 / 账号信息入口
|
- 不再提供 `AuthGate` 层右上角固定悬浮的全局登录 / 账号信息入口
|
||||||
- 登录触发统一来自页面内受保护动作、个人页、存档页等明确入口
|
- 登录触发统一来自页面内受保护动作、个人页、存档页等明确入口
|
||||||
- 账号信息面板只通过页面内按钮打开,不在平台右上角常驻悬浮
|
- 账号信息面板只通过页面内按钮打开,不在平台右上角常驻悬浮
|
||||||
|
- 未登录移动端底部导航不展示“我的”时,平台页头必须保留一个直接可点的 `登录` 入口,避免用户只能通过受保护动作被动触发弹窗
|
||||||
|
- 桌面端平台页头的账号胶囊在未登录时主文案必须直接显示 `登录`,不能只显示“进入账户”这类弱入口
|
||||||
|
|
||||||
## 4.2 平台首页数据加载
|
## 4.2 平台首页数据加载
|
||||||
|
|
||||||
@@ -222,3 +224,4 @@
|
|||||||
3. 未登录选择 RPG 创作类型时,直接弹出登录弹窗,登录后自动进入创作工作台。
|
3. 未登录选择 RPG 创作类型时,直接弹出登录弹窗,登录后自动进入创作工作台。
|
||||||
4. 登录弹窗内没有介绍性大段文字,只剩必要输入与按钮。
|
4. 登录弹窗内没有介绍性大段文字,只剩必要输入与按钮。
|
||||||
5. 未登录态首页不会因个人接口失败而出现“读取个人看板失败”“读取作品库失败”之类报错。
|
5. 未登录态首页不会因个人接口失败而出现“读取个人看板失败”“读取作品库失败”之类报错。
|
||||||
|
6. 未登录移动端首页页头存在明确 `登录` 入口,点击后打开同一个登录弹窗。
|
||||||
|
|||||||
@@ -157,6 +157,55 @@ function renderProfileView(onRechargeSuccess = vi.fn()) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function renderLoggedOutHomeView(openLoginModal = vi.fn()) {
|
||||||
|
return render(
|
||||||
|
<AuthUiContext.Provider
|
||||||
|
value={{
|
||||||
|
user: null,
|
||||||
|
canAccessProtectedData: false,
|
||||||
|
openLoginModal,
|
||||||
|
requireAuth: vi.fn(),
|
||||||
|
openSettingsModal: vi.fn(),
|
||||||
|
openAccountModal: vi.fn(),
|
||||||
|
logout: vi.fn(async () => undefined),
|
||||||
|
musicVolume: 0.42,
|
||||||
|
setMusicVolume: vi.fn(),
|
||||||
|
platformTheme: 'light',
|
||||||
|
setPlatformTheme: vi.fn(),
|
||||||
|
isHydratingSettings: false,
|
||||||
|
isPersistingSettings: false,
|
||||||
|
settingsError: null,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<RpgEntryHomeView
|
||||||
|
activeTab="home"
|
||||||
|
onTabChange={vi.fn()}
|
||||||
|
hasSavedGame={false}
|
||||||
|
savedSnapshot={null}
|
||||||
|
saveEntries={[]}
|
||||||
|
saveError={null}
|
||||||
|
featuredEntries={[]}
|
||||||
|
latestEntries={[]}
|
||||||
|
myEntries={[]}
|
||||||
|
historyEntries={[]}
|
||||||
|
profileDashboard={null}
|
||||||
|
isLoadingPlatform={false}
|
||||||
|
isLoadingDashboard={false}
|
||||||
|
isResumingSaveWorldKey={null}
|
||||||
|
platformError={null}
|
||||||
|
dashboardError={null}
|
||||||
|
onContinueGame={vi.fn()}
|
||||||
|
onResumeSave={vi.fn()}
|
||||||
|
onOpenCreateWorld={vi.fn()}
|
||||||
|
onOpenCreateTypePicker={vi.fn()}
|
||||||
|
onOpenGalleryDetail={vi.fn()}
|
||||||
|
onOpenLibraryDetail={vi.fn()}
|
||||||
|
onSearchPublicCode={vi.fn()}
|
||||||
|
/>
|
||||||
|
</AuthUiContext.Provider>,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
vi.clearAllMocks();
|
vi.clearAllMocks();
|
||||||
});
|
});
|
||||||
@@ -175,3 +224,13 @@ test('opens recharge modal and submits points product', async () => {
|
|||||||
|
|
||||||
await waitFor(() => expect(onRechargeSuccess).toHaveBeenCalledTimes(1));
|
await waitFor(() => expect(onRechargeSuccess).toHaveBeenCalledTimes(1));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('shows a reachable login entry in logged out mobile shell', async () => {
|
||||||
|
const user = userEvent.setup();
|
||||||
|
const openLoginModal = vi.fn();
|
||||||
|
|
||||||
|
renderLoggedOutHomeView(openLoginModal);
|
||||||
|
await user.click(screen.getByRole('button', { name: '登录' }));
|
||||||
|
|
||||||
|
expect(openLoginModal).toHaveBeenCalledTimes(1);
|
||||||
|
});
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import {
|
|||||||
Copy,
|
Copy,
|
||||||
Crown,
|
Crown,
|
||||||
House,
|
House,
|
||||||
|
LogIn,
|
||||||
MessageCircle,
|
MessageCircle,
|
||||||
Pencil,
|
Pencil,
|
||||||
Search,
|
Search,
|
||||||
@@ -1896,8 +1897,18 @@ export function RpgEntryHomeView({
|
|||||||
if (!isDesktopLayout) {
|
if (!isDesktopLayout) {
|
||||||
return (
|
return (
|
||||||
<div className="platform-mobile-entry-shell flex h-full min-h-0 min-w-0 flex-col overflow-hidden">
|
<div className="platform-mobile-entry-shell flex h-full min-h-0 min-w-0 flex-col overflow-hidden">
|
||||||
<div className="mb-3 shrink-0 px-0.5">
|
<div className="mb-3 flex shrink-0 items-center justify-between gap-3 px-0.5">
|
||||||
<RpgEntryBrandLogo />
|
<RpgEntryBrandLogo />
|
||||||
|
{!isAuthenticated ? (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={openUserSurface}
|
||||||
|
className="platform-button platform-button--primary shrink-0 px-3 py-2 text-xs"
|
||||||
|
>
|
||||||
|
<LogIn className="h-3.5 w-3.5" />
|
||||||
|
登录
|
||||||
|
</button>
|
||||||
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="platform-tab-panel-stack min-w-0 flex-1">
|
<div className="platform-tab-panel-stack min-w-0 flex-1">
|
||||||
@@ -1912,7 +1923,7 @@ export function RpgEntryHomeView({
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className={`platform-bottom-nav grid ${visibleTabs.length === 5 ? 'grid-cols-5' : visibleTabs.length === 4 ? 'grid-cols-4' : 'grid-cols-2'}`}
|
className={`platform-bottom-nav grid ${visibleTabs.length === 5 ? 'grid-cols-5' : visibleTabs.length === 4 ? 'grid-cols-4' : visibleTabs.length === 3 ? 'grid-cols-3' : 'grid-cols-2'}`}
|
||||||
>
|
>
|
||||||
{visibleTabs.map((tab) => (
|
{visibleTabs.map((tab) => (
|
||||||
<PlatformTabButton
|
<PlatformTabButton
|
||||||
@@ -2005,10 +2016,10 @@ export function RpgEntryHomeView({
|
|||||||
</span>
|
</span>
|
||||||
<span className="min-w-0">
|
<span className="min-w-0">
|
||||||
<span className="block truncate text-sm font-semibold text-[var(--platform-text-strong)]">
|
<span className="block truncate text-sm font-semibold text-[var(--platform-text-strong)]">
|
||||||
{authUi?.user?.displayName || '进入账户'}
|
{authUi?.user?.displayName || '登录'}
|
||||||
</span>
|
</span>
|
||||||
<span className="block truncate text-xs text-[var(--platform-text-soft)]">
|
<span className="block truncate text-xs text-[var(--platform-text-soft)]">
|
||||||
{authUi?.user ? publicUserCode : '登录后同步作品与进度'}
|
{authUi?.user ? publicUserCode : '账号入口'}
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
Reference in New Issue
Block a user