17.05.2026 funny

This commit is contained in:
2026-05-17 13:06:18 +03:00
parent 73d1fd9135
commit bd3d747ede
10 changed files with 157 additions and 39 deletions

View File

@@ -1,14 +1,59 @@
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}>
<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.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" />
@@ -16,10 +61,19 @@ export function ProfileAvatar() {
</svg>
</div>
</div>
<input
ref={inputRef}
type="file"
accept="image/*"
onChange={handleFileChange}
hidden
/>
<div className={styles.addPhoto}>
<Button variant="ghost">ДОБАВИТЬ ФОТО</Button>
<Button variant="ghost" onClick={openPicker} disabled={isPending}>
{isPending ? 'ЗАГРУЗКА...' : 'ДОБАВИТЬ ФОТО'}
</Button>
</div>
<Button variant="danger">УДАЛИТЬ ФОТО</Button>
{error && <span className={styles.error}>{error}</span>}
</div>
)
}