remove avator edit
This commit is contained in:
parent
315ee14087
commit
16ed3560a3
@ -80,10 +80,8 @@
|
|||||||
"passwordMinLength": "Password must be at least 6 characters",
|
"passwordMinLength": "Password must be at least 6 characters",
|
||||||
"passwordUpdatedSuccessfully": "Password updated successfully",
|
"passwordUpdatedSuccessfully": "Password updated successfully",
|
||||||
"failedToUpdatePassword": "Failed to update password",
|
"failedToUpdatePassword": "Failed to update password",
|
||||||
"uploading": "Uploading...",
|
"googleAvatar": "Avatar from Google account",
|
||||||
"clickCameraToUpload": "Click the camera icon to upload a new picture",
|
"defaultAvatar": "Default avatar",
|
||||||
"avatarUpdatedSuccessfully": "Avatar updated successfully",
|
|
||||||
"failedToUploadAvatar": "Failed to upload avatar",
|
|
||||||
"noUsernameSet": "No username set",
|
"noUsernameSet": "No username set",
|
||||||
"enterUsername": "Enter username",
|
"enterUsername": "Enter username",
|
||||||
"enterEmail": "Enter email",
|
"enterEmail": "Enter email",
|
||||||
|
@ -80,10 +80,8 @@
|
|||||||
"passwordMinLength": "密码至少需要6个字符",
|
"passwordMinLength": "密码至少需要6个字符",
|
||||||
"passwordUpdatedSuccessfully": "密码更新成功",
|
"passwordUpdatedSuccessfully": "密码更新成功",
|
||||||
"failedToUpdatePassword": "密码更新失败",
|
"failedToUpdatePassword": "密码更新失败",
|
||||||
"uploading": "上传中...",
|
"googleAvatar": "来自Google账号的头像",
|
||||||
"clickCameraToUpload": "点击相机图标上传新头像",
|
"defaultAvatar": "默认头像",
|
||||||
"avatarUpdatedSuccessfully": "头像更新成功",
|
|
||||||
"failedToUploadAvatar": "头像上传失败",
|
|
||||||
"noUsernameSet": "未设置用户名",
|
"noUsernameSet": "未设置用户名",
|
||||||
"enterUsername": "输入用户名",
|
"enterUsername": "输入用户名",
|
||||||
"enterEmail": "输入邮箱",
|
"enterEmail": "输入邮箱",
|
||||||
|
@ -13,7 +13,7 @@ import { Avatar } from '@/components/ui/avatar'
|
|||||||
import { LoadingSpinner, LoadingOverlay } from '@/components/ui/loading-spinner'
|
import { LoadingSpinner, LoadingOverlay } from '@/components/ui/loading-spinner'
|
||||||
import { FullScreenLoading } from '@/components/ui/full-screen-loading'
|
import { FullScreenLoading } from '@/components/ui/full-screen-loading'
|
||||||
import { AvatarSkeleton, FormFieldSkeleton, TextAreaSkeleton } from '@/components/ui/skeleton'
|
import { AvatarSkeleton, FormFieldSkeleton, TextAreaSkeleton } from '@/components/ui/skeleton'
|
||||||
import { Camera, Save, Eye, EyeOff, Globe, CreditCard, Crown, Star } from 'lucide-react'
|
import { Save, Eye, EyeOff, Globe, CreditCard, Crown, Star } from 'lucide-react'
|
||||||
|
|
||||||
interface UserProfile {
|
interface UserProfile {
|
||||||
id: string
|
id: string
|
||||||
@ -83,7 +83,6 @@ export default function ProfilePage() {
|
|||||||
const [isLoading, setIsLoading] = useState(false)
|
const [isLoading, setIsLoading] = useState(false)
|
||||||
const [profileLoading, setProfileLoading] = useState(true)
|
const [profileLoading, setProfileLoading] = useState(true)
|
||||||
const [fieldLoading, setFieldLoading] = useState<{ [key: string]: boolean }>({})
|
const [fieldLoading, setFieldLoading] = useState<{ [key: string]: boolean }>({})
|
||||||
const [avatarUploading, setAvatarUploading] = useState(false)
|
|
||||||
const [creditInfo, setCreditInfo] = useState<CreditInfo | null>(null)
|
const [creditInfo, setCreditInfo] = useState<CreditInfo | null>(null)
|
||||||
|
|
||||||
const supabase = createClient()
|
const supabase = createClient()
|
||||||
@ -219,53 +218,6 @@ export default function ProfilePage() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const uploadAvatar = async (event: React.ChangeEvent<HTMLInputElement>) => {
|
|
||||||
const file = event.target.files?.[0]
|
|
||||||
if (!file || !user) return
|
|
||||||
|
|
||||||
setAvatarUploading(true)
|
|
||||||
setIsLoading(true)
|
|
||||||
setSaveStatus({ type: null, message: '' })
|
|
||||||
|
|
||||||
try {
|
|
||||||
// Create a unique filename for future use with actual file storage
|
|
||||||
// const fileExt = file.name.split('.').pop()
|
|
||||||
// const fileName = `${user.id}-${Date.now()}.${fileExt}`
|
|
||||||
|
|
||||||
// For now, we'll use a placeholder upload since we need to configure storage
|
|
||||||
// In a real implementation, you would upload to Supabase Storage or Cloudflare R2
|
|
||||||
const reader = new FileReader()
|
|
||||||
reader.onload = async (e) => {
|
|
||||||
const dataUrl = e.target?.result as string
|
|
||||||
|
|
||||||
// Update avatar using our API
|
|
||||||
const response = await fetch('/api/users/profile', {
|
|
||||||
method: 'PUT',
|
|
||||||
headers: { 'Content-Type': 'application/json' },
|
|
||||||
body: JSON.stringify({
|
|
||||||
userId: user.id,
|
|
||||||
avatar: dataUrl
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
if (!response.ok) {
|
|
||||||
const errorData = await response.json()
|
|
||||||
throw new Error(errorData.error || 'Failed to update avatar')
|
|
||||||
}
|
|
||||||
|
|
||||||
await loadProfile()
|
|
||||||
setSaveStatus({ type: 'success', message: t('avatarUpdatedSuccessfully') })
|
|
||||||
setIsLoading(false)
|
|
||||||
setAvatarUploading(false)
|
|
||||||
}
|
|
||||||
reader.readAsDataURL(file)
|
|
||||||
|
|
||||||
} catch (error: unknown) {
|
|
||||||
setSaveStatus({ type: 'error', message: (error instanceof Error ? error.message : 'Unknown error') || t('failedToUploadAvatar') })
|
|
||||||
setIsLoading(false)
|
|
||||||
setAvatarUploading(false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (loading) {
|
if (loading) {
|
||||||
return <FullScreenLoading isVisible={true} message={t('loadingStudio')} />
|
return <FullScreenLoading isVisible={true} message={t('loadingStudio')} />
|
||||||
@ -311,38 +263,20 @@ export default function ProfilePage() {
|
|||||||
{profileLoading ? (
|
{profileLoading ? (
|
||||||
<AvatarSkeleton />
|
<AvatarSkeleton />
|
||||||
) : (
|
) : (
|
||||||
<LoadingOverlay isLoading={avatarUploading}>
|
|
||||||
<div className="bg-card p-6 rounded-lg border border-border">
|
<div className="bg-card p-6 rounded-lg border border-border">
|
||||||
<h2 className="text-xl font-semibold text-foreground mb-4">{t('profilePicture')}</h2>
|
<h2 className="text-xl font-semibold text-foreground mb-4">{t('profilePicture')}</h2>
|
||||||
<div className="flex flex-col items-center">
|
<div className="flex flex-col items-center">
|
||||||
<div className="relative">
|
|
||||||
<Avatar
|
<Avatar
|
||||||
src={profile?.avatar}
|
src={user?.user_metadata?.avatar_url || profile?.avatar}
|
||||||
alt="Profile Avatar"
|
alt="Profile Avatar"
|
||||||
size={96}
|
size={96}
|
||||||
className="w-24 h-24"
|
className="w-24 h-24"
|
||||||
/>
|
/>
|
||||||
<label className={`absolute -bottom-2 -right-2 bg-primary text-primary-foreground p-2 rounded-full cursor-pointer hover:bg-primary/90 ${avatarUploading ? 'pointer-events-none opacity-50' : ''}`}>
|
|
||||||
{avatarUploading ? (
|
|
||||||
<LoadingSpinner size="sm" className="text-primary-foreground" />
|
|
||||||
) : (
|
|
||||||
<Camera className="w-4 h-4" />
|
|
||||||
)}
|
|
||||||
<input
|
|
||||||
type="file"
|
|
||||||
accept="image/*"
|
|
||||||
className="hidden"
|
|
||||||
onChange={uploadAvatar}
|
|
||||||
disabled={isLoading || avatarUploading}
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<p className="text-sm text-muted-foreground mt-2 text-center">
|
<p className="text-sm text-muted-foreground mt-2 text-center">
|
||||||
{avatarUploading ? t('uploading') : t('clickCameraToUpload')}
|
{user?.user_metadata?.avatar_url ? t('googleAvatar') : t('defaultAvatar')}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</LoadingOverlay>
|
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Subscription Status */}
|
{/* Subscription Status */}
|
||||||
@ -428,7 +362,7 @@ export default function ProfilePage() {
|
|||||||
variant="outline"
|
variant="outline"
|
||||||
size="sm"
|
size="sm"
|
||||||
onClick={() => setIsEditing(prev => ({ ...prev, username: true }))}
|
onClick={() => setIsEditing(prev => ({ ...prev, username: true }))}
|
||||||
disabled={isLoading || avatarUploading}
|
disabled={isLoading}
|
||||||
>
|
>
|
||||||
{tCommon('edit')}
|
{tCommon('edit')}
|
||||||
</Button>
|
</Button>
|
||||||
@ -489,7 +423,7 @@ export default function ProfilePage() {
|
|||||||
variant="outline"
|
variant="outline"
|
||||||
size="sm"
|
size="sm"
|
||||||
onClick={() => setIsEditing(prev => ({ ...prev, email: true }))}
|
onClick={() => setIsEditing(prev => ({ ...prev, email: true }))}
|
||||||
disabled={isLoading || avatarUploading}
|
disabled={isLoading}
|
||||||
>
|
>
|
||||||
{tCommon('edit')}
|
{tCommon('edit')}
|
||||||
</Button>
|
</Button>
|
||||||
@ -551,7 +485,7 @@ export default function ProfilePage() {
|
|||||||
variant="outline"
|
variant="outline"
|
||||||
size="sm"
|
size="sm"
|
||||||
onClick={() => setIsEditing(prev => ({ ...prev, bio: true }))}
|
onClick={() => setIsEditing(prev => ({ ...prev, bio: true }))}
|
||||||
disabled={isLoading || avatarUploading}
|
disabled={isLoading}
|
||||||
>
|
>
|
||||||
{tCommon('edit')}
|
{tCommon('edit')}
|
||||||
</Button>
|
</Button>
|
||||||
@ -627,7 +561,7 @@ export default function ProfilePage() {
|
|||||||
updateProfile('language', newLanguage)
|
updateProfile('language', newLanguage)
|
||||||
}}
|
}}
|
||||||
className="w-full px-3 py-2 border border-border rounded-md bg-input text-foreground focus:outline-none focus:ring-2 focus:ring-ring"
|
className="w-full px-3 py-2 border border-border rounded-md bg-input text-foreground focus:outline-none focus:ring-2 focus:ring-ring"
|
||||||
disabled={isLoading || fieldLoading.language || avatarUploading}
|
disabled={isLoading || fieldLoading.language}
|
||||||
>
|
>
|
||||||
<option value="en">{t('english')}</option>
|
<option value="en">{t('english')}</option>
|
||||||
<option value="zh">{t('chinese')}</option>
|
<option value="zh">{t('chinese')}</option>
|
||||||
@ -649,7 +583,7 @@ export default function ProfilePage() {
|
|||||||
variant="outline"
|
variant="outline"
|
||||||
size="sm"
|
size="sm"
|
||||||
onClick={() => setIsEditing(prev => ({ ...prev, password: true }))}
|
onClick={() => setIsEditing(prev => ({ ...prev, password: true }))}
|
||||||
disabled={isLoading || avatarUploading}
|
disabled={isLoading}
|
||||||
>
|
>
|
||||||
{t('changePassword')}
|
{t('changePassword')}
|
||||||
</Button>
|
</Button>
|
||||||
@ -760,7 +694,7 @@ export default function ProfilePage() {
|
|||||||
variant="outline"
|
variant="outline"
|
||||||
size="sm"
|
size="sm"
|
||||||
onClick={() => setIsEditing(prev => ({ ...prev, versionLimit: true }))}
|
onClick={() => setIsEditing(prev => ({ ...prev, versionLimit: true }))}
|
||||||
disabled={isLoading || avatarUploading}
|
disabled={isLoading}
|
||||||
>
|
>
|
||||||
{tCommon('edit')}
|
{tCommon('edit')}
|
||||||
</Button>
|
</Button>
|
||||||
|
Loading…
Reference in New Issue
Block a user