feat: simplify workspace account UI and geo language defaults

This commit is contained in:
2026-03-27 02:25:26 +08:00
parent 2bcf32d678
commit 6a6f7e970c
4 changed files with 8 additions and 56 deletions

View File

@@ -1,5 +1,5 @@
import React, { useState, useRef, useCallback, useEffect } from 'react'; import React, { useState, useRef, useCallback, useEffect } from 'react';
import { Upload, LogOut, FileText, Clock, ChevronLeft, ChevronRight, MousePointerClick, FileUp, ClipboardPaste, Loader2 } from 'lucide-react'; import { Upload, FileText, Clock, ChevronLeft, ChevronRight, MousePointerClick, FileUp, ClipboardPaste, Loader2 } from 'lucide-react';
import { useAuth } from '../contexts/AuthContext'; import { useAuth } from '../contexts/AuthContext';
import { useLanguage } from '../contexts/LanguageContext'; import { useLanguage } from '../contexts/LanguageContext';
import { FileRecord } from '../types'; import { FileRecord } from '../types';
@@ -37,12 +37,11 @@ export default function LeftSidebar({
historyEnabled, historyEnabled,
onToggleHistory, onToggleHistory,
}: LeftSidebarProps) { }: LeftSidebarProps) {
const { user, signOut } = useAuth(); const { user } = useAuth();
const { t } = useLanguage(); const { t } = useLanguage();
const [isDragging, setIsDragging] = useState(false); const [isDragging, setIsDragging] = useState(false);
const fileInputRef = useRef<HTMLInputElement>(null); const fileInputRef = useRef<HTMLInputElement>(null);
const listRef = useRef<HTMLDivElement>(null); const listRef = useRef<HTMLDivElement>(null);
const displayName = user?.username?.trim() || user?.email || '';
// Handle scroll to load more // Handle scroll to load more
const handleScroll = useCallback(() => { const handleScroll = useCallback(() => {
@@ -308,29 +307,6 @@ export default function LeftSidebar({
)} )}
</div> </div>
</div> </div>
{/* Bottom Area: User info (only shown when logged in) */}
{user && (
<div className="p-4 border-t border-gray-100 bg-gray-50/30">
<div className="flex items-center gap-3 p-2 rounded-lg bg-white border border-gray-100 shadow-sm">
<div className="w-8 h-8 bg-gray-900 rounded-full flex items-center justify-center flex-shrink-0">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12 12C14.21 12 16 10.21 16 8C16 5.79 14.21 4 12 4C9.79 4 8 5.79 8 8C8 10.21 9.79 12 12 12ZM12 14C9.33 14 4 15.34 4 18V20H20V18C20 15.34 14.67 14 12 14Z" fill="white" />
</svg>
</div>
<div className="flex-1 min-w-0">
<p className="text-sm font-medium text-gray-900 truncate">{displayName}</p>
</div>
<button
onClick={() => signOut()}
className="p-1.5 text-gray-400 hover:text-red-500 hover:bg-red-50 rounded-md transition-colors"
title={t.common.logout}
>
<LogOut size={16} />
</button>
</div>
</div>
)}
</div> </div>
</> </>
); );

View File

@@ -111,14 +111,6 @@ export default function MarketingNavbar() {
</div> </div>
{userMenuOpen && ( {userMenuOpen && (
<div className="nav-user-menu" style={{ display: 'block' }}> <div className="nav-user-menu" style={{ display: 'block' }}>
<div className="nav-menu-divider" />
<Link to="/app" className="nav-menu-item" onClick={() => setUserMenuOpen(false)}>
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8">
<rect x="2" y="3" width="20" height="14" rx="2" />
<path d="M8 21h8M12 17v4" />
</svg>
{language === 'zh' ? '启动应用' : 'Launch App'}
</Link>
<div className="nav-menu-divider" /> <div className="nav-menu-divider" />
<button <button
className="nav-menu-item nav-menu-logout" className="nav-menu-item nav-menu-logout"

View File

@@ -12,12 +12,11 @@ interface LanguageContextType {
const LanguageContext = createContext<LanguageContextType | undefined>(undefined); const LanguageContext = createContext<LanguageContextType | undefined>(undefined);
export const LanguageProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => { export const LanguageProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
// 初始化语言:优先使用 localStorage否则使用浏览器语言作为临时值 // 初始化语言:优先使用 localStorage否则默认英文,后续由 IP 检测决定
const [language, setLanguageState] = useState<Language>(() => { const [language, setLanguageState] = useState<Language>(() => {
const saved = localStorage.getItem('language'); const saved = localStorage.getItem('language');
if (saved === 'en' || saved === 'zh') return saved; if (saved === 'en' || saved === 'zh') return saved;
// 临时使用浏览器语言后续会被IP检测覆盖如果没有保存的语言 return 'en';
return navigator.language.startsWith('zh') ? 'zh' : 'en';
}); });
// 检测IP地理位置并设置语言仅在首次加载且没有保存的语言时 // 检测IP地理位置并设置语言仅在首次加载且没有保存的语言时
@@ -30,16 +29,14 @@ export const LanguageProvider: React.FC<{ children: React.ReactNode }> = ({ chil
return; return;
} }
// 异步检测IP并设置语言 // 异步检测IP并设置语言:中国 IP 显示中文,其余显示英文
detectLanguageByIP() detectLanguageByIP()
.then((detectedLang) => { .then((detectedLang) => {
setLanguageState(detectedLang); setLanguageState(detectedLang);
updatePageMeta(detectedLang); // Update meta tags after detection updatePageMeta(detectedLang); // Update meta tags after detection
// 注意:这里不保存到 localStorage让用户首次访问时使用IP检测的结果
// 如果用户手动切换语言,才会保存到 localStorage
}) })
.catch((error) => { .catch((error) => {
// IP检测失败时保持使用浏览器语言检测的结果 // IP检测失败时保持英文默认值
console.warn('Failed to detect language by IP:', error); console.warn('Failed to detect language by IP:', error);
updatePageMeta(language); // Update with fallback language updatePageMeta(language); // Update with fallback language
}); });

View File

@@ -47,24 +47,11 @@ export async function detectCountryByIP(): Promise<string | null> {
/** /**
* 根据国家代码判断应该使用的语言 * 根据国家代码判断应该使用的语言
* *
* @param countryCode 国家代码(如 'CN', 'TW', 'HK', 'SG' 等 * @param countryCode 国家代码(如 'CN', 'US'
* @returns 'zh' | 'en' 推荐的语言 * @returns 'zh' | 'en' 推荐的语言
*/ */
export function getLanguageByCountry(countryCode: string | null): 'zh' | 'en' { export function getLanguageByCountry(countryCode: string | null): 'zh' | 'en' {
if (!countryCode) { return countryCode === 'CN' ? 'zh' : 'en';
return 'en';
}
// 中文地区列表
const chineseRegions = [
'CN', // 中国大陆
'TW', // 台湾
'HK', // 香港
'MO', // 澳门
'SG', // 新加坡(主要使用中文)
];
return chineseRegions.includes(countryCode) ? 'zh' : 'en';
} }
/** /**