80 lines
2.6 KiB
TypeScript
80 lines
2.6 KiB
TypeScript
import { useRef, useState } from 'react'
|
||
import { Button } from '@shared/ui'
|
||
import { useMe, useUploadAvatar } from '@features/auth'
|
||
import styles from './ProfileAvatar.module.css'
|
||
|
||
function fileToBase64(file: File): Promise<string> {
|
||
return new Promise((resolve, reject) => {
|
||
const reader = new FileReader()
|
||
reader.onload = () => {
|
||
const result = reader.result as string
|
||
const comma = result.indexOf(',')
|
||
resolve(comma >= 0 ? result.slice(comma + 1) : result)
|
||
}
|
||
reader.onerror = () => reject(reader.error)
|
||
reader.readAsDataURL(file)
|
||
})
|
||
}
|
||
|
||
export function ProfileAvatar() {
|
||
const { data } = useMe()
|
||
const { mutateAsync: upload, isPending } = useUploadAvatar()
|
||
const inputRef = useRef<HTMLInputElement>(null)
|
||
const [error, setError] = useState<string | null>(null)
|
||
|
||
const avatarLink = data?.avatar_link ?? null
|
||
|
||
const openPicker = () => {
|
||
if (isPending) return
|
||
inputRef.current?.click()
|
||
}
|
||
|
||
const handleFileChange = async (e: React.ChangeEvent<HTMLInputElement>) => {
|
||
const file = e.target.files?.[0]
|
||
e.target.value = ''
|
||
if (!file) return
|
||
|
||
setError(null)
|
||
try {
|
||
const photo_base64 = await fileToBase64(file)
|
||
await upload({ photo_base64, decoded_bytes: String(file.size) })
|
||
} catch {
|
||
setError('Не удалось загрузить фото')
|
||
}
|
||
}
|
||
|
||
return (
|
||
<div className={styles.col}>
|
||
<div className={styles.avatar} onClick={openPicker}>
|
||
{avatarLink ? (
|
||
<img src={avatarLink} alt="avatar" className={styles.avatarImg} />
|
||
) : (
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round">
|
||
<circle cx="12" cy="8" r="4" />
|
||
<path d="M4 20c0-4 4-7 8-7s8 3 8 7" />
|
||
</svg>
|
||
)}
|
||
<div className={styles.overlay}>
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="#fff" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round">
|
||
<path d="M23 19a2 2 0 01-2 2H3a2 2 0 01-2-2V8a2 2 0 012-2h4l2-3h6l2 3h4a2 2 0 012 2z" />
|
||
<circle cx="12" cy="13" r="4" />
|
||
</svg>
|
||
</div>
|
||
</div>
|
||
<input
|
||
ref={inputRef}
|
||
type="file"
|
||
accept="image/*"
|
||
onChange={handleFileChange}
|
||
hidden
|
||
/>
|
||
<div className={styles.addPhoto}>
|
||
<Button variant="ghost" onClick={openPicker} disabled={isPending}>
|
||
{isPending ? 'ЗАГРУЗКА...' : 'ДОБАВИТЬ ФОТО'}
|
||
</Button>
|
||
</div>
|
||
{error && <span className={styles.error}>{error}</span>}
|
||
</div>
|
||
)
|
||
}
|