Skip to content

Commit 68c1ddd

Browse files
committed
feat: profile
1 parent 4a0ddb5 commit 68c1ddd

8 files changed

Lines changed: 447 additions & 8 deletions

File tree

README.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,24 @@
5353
4. UI обновляется без задержек
5454

5555
Подробное описание: см. [STATE_OPTIMIZATION.md](./STATE_OPTIMIZATION.md)
56+
57+
### Профиль пользователя
58+
59+
Приложение включает страницу профиля с возможностью загрузки аватарки:
60+
61+
- ✅ Просмотр и редактирование профиля
62+
- ✅ Загрузка аватарки (JPG, PNG, GIF, WebP до 2MB)
63+
- ✅ Отображаемое имя и настройки пользователя
64+
- ✅ Аватарки в шапке и профиле
65+
- ✅ Интеграция с GitHub для хранения изображений
66+
67+
**Как использовать:**
68+
1. Клик на имя пользователя в шапке
69+
2. Переход на страницу профиля `/profile`
70+
3. Загрузка аватарки через иконку камеры
71+
4. Редактирование отображаемого имени
72+
73+
Подробное описание: см. [USER_PROFILE.md](./USER_PROFILE.md)
5674
```
5775
5876
Подробное описание: см. [TRANSLITERATION.md](./TRANSLITERATION.md)

USER_PROFILE.md

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
# Страница профиля пользователя
2+
3+
Приложение теперь включает полнофункциональную страницу профиля с возможностью загрузки аватарки.
4+
5+
## Новые возможности
6+
7+
### 1. Страница профиля (`/profile`)
8+
9+
- ✅ Просмотр информации о профиле
10+
- ✅ Редактирование отображаемого имени
11+
- ✅ Загрузка аватарки
12+
- ✅ Просмотр даты регистрации
13+
- ✅ Отображение настроек языка и темы
14+
- ✅ Выход из аккаунта
15+
16+
### 2. Загрузка аватарки
17+
18+
**Технические детали:**
19+
- Поддерживаемые форматы: JPG, PNG, GIF, WebP
20+
- Максимальный размер: 2MB
21+
- Рекомендуемый размер: 200x200px или больше (квадратные)
22+
- Хранение: в папке `avatars/` репозитория
23+
- Доступ: через GitHub raw content URLs
24+
25+
**Процесс загрузки:**
26+
1. Пользователь выбирает изображение
27+
2. Валидация типа файла и размера
28+
3. Конвертация в base64
29+
4. Загрузка в GitHub с уникальным именем файла
30+
5. Обновление профиля пользователя
31+
6. Перезагрузка страницы для отображения аватарки
32+
33+
### 3. Обновленный интерфейс пользователя
34+
35+
**Интерфейс User:**
36+
```typescript
37+
interface User {
38+
username: string;
39+
displayName: string;
40+
avatar?: string; // Новое поле
41+
joinedAt: string;
42+
lang: string;
43+
theme: string;
44+
}
45+
```
46+
47+
**Отображение в шапке:**
48+
- Аватарка вместо стандартной иконки
49+
- Ссылка на профиль при клике на имя пользователя
50+
- Сохранение существующего функционала выхода
51+
52+
### 4. Навигация
53+
54+
- Добавлен маршрут `/profile`
55+
- Защищенная страница (требует авторизации)
56+
- Кнопка "Назад" для возврата на главную
57+
58+
## Техническая реализация
59+
60+
### GitHub API интеграция
61+
62+
Обновлена функция `putFile()` для поддержки бинарных файлов:
63+
64+
```typescript
65+
export async function putFile(
66+
path: string,
67+
content: string,
68+
message: string,
69+
isBase64: boolean = false, // Новый параметр
70+
sha?: string
71+
)
72+
```
73+
74+
### Хранение аватарок
75+
76+
- Папка: `avatars/`
77+
- Именование: `avatar-{username}-{timestamp}.{extension}`
78+
- URL: `https://raw.githubusercontent.com/feekool/feekool.github.io/master/avatars/{filename}`
79+
80+
### Валидация
81+
82+
- **Тип файла:** только изображения (`image/*`)
83+
- **Размер файла:** максимум 2MB
84+
- **Обработка ошибок:** пользовательские сообщения
85+
86+
## Пользовательский опыт
87+
88+
### Доступ к профилю
89+
90+
1. Клик на имя пользователя в шапке
91+
2. Переход на страницу `/profile`
92+
3. Просмотр и редактирование профиля
93+
94+
### Загрузка аватарки
95+
96+
1. Клик на иконку камеры на аватарке
97+
2. Выбор файла изображения
98+
3. Автоматическая загрузка и обновление
99+
100+
### Редактирование профиля
101+
102+
1. Клик "Edit" рядом с именем
103+
2. Ввод нового отображаемого имени
104+
3. Сохранение изменений
105+
106+
## Совместимость
107+
108+
- ✅ Поддержка всех браузеров с File API
109+
- ✅ Мобильные устройства (iOS Safari, Android Chrome)
110+
- ✅ Темная и светлая темы
111+
- ✅ Многоязычность (EN/RU)
112+
113+
## Безопасность
114+
115+
- Валидация файлов на клиенте
116+
- Ограничение размера файлов
117+
- Безопасные имена файлов
118+
- Публичный доступ к аватаркам через GitHub
119+
120+
## Будущие улучшения
121+
122+
Возможно добавить:
123+
- Обрезка изображений перед загрузкой
124+
- Несколько аватарок для выбора
125+
- Анимации загрузки
126+
- Предпросмотр перед сохранением
127+
- Удаление аватарки

src/App.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { Layout } from './components/Layout';
77
import { AuthPage } from './pages/AuthPage';
88
import { HomePage } from './pages/HomePage';
99
import { ForumPage } from './pages/ForumPage';
10-
import { TopicPage } from './pages/TopicPage';
10+
import { ProfilePage } from './pages/ProfilePage';
1111

1212
// Error Boundary component
1313
class ErrorBoundary extends React.Component<
@@ -72,6 +72,7 @@ function AppRoutes() {
7272
<Route path="/auth" element={<AuthPage />} />
7373
<Route element={<Layout />}>
7474
<Route path="/" element={<HomePage />} />
75+
<Route path="/profile" element={<ProfilePage />} />
7576
<Route path="/forum/:slug" element={<ForumPage />} />
7677
<Route path="/topic/:id" element={<TopicPage />} />
7778
</Route>

src/components/Layout.tsx

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,23 @@ export function Layout() {
3131

3232
{user ?
3333
<div className="flex items-center gap-4 ml-4 pl-4 border-l border-gray-200 dark:border-gray-700">
34-
<div className="flex items-center gap-2 text-sm font-medium">
35-
<div className="w-8 h-8 rounded-full bg-blue-100 dark:bg-blue-900 flex items-center justify-center text-blue-600 dark:text-blue-400">
36-
<UserIcon className="w-4 h-4" />
34+
<Link
35+
to="/profile"
36+
className="flex items-center gap-2 text-sm font-medium hover:text-blue-600 dark:hover:text-blue-400 transition-colors"
37+
>
38+
<div className="w-8 h-8 rounded-full bg-blue-100 dark:bg-blue-900 flex items-center justify-center overflow-hidden">
39+
{user.avatar ? (
40+
<img
41+
src={user.avatar}
42+
alt={user.displayName}
43+
className="w-full h-full object-cover"
44+
/>
45+
) : (
46+
<UserIcon className="w-4 h-4" />
47+
)}
3748
</div>
3849
<span className="hidden sm:inline">{user.displayName}</span>
39-
</div>
50+
</Link>
4051
<button
4152
onClick={handleLogout}
4253
className="p-2 text-gray-500 hover:text-red-600 dark:text-gray-400 dark:hover:text-red-400 transition-colors"

src/lib/auth.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { parseFrontmatter, stringifyFrontmatter } from './utils';
55
export interface User {
66
username: string;
77
displayName: string;
8+
avatar?: string; // URL to avatar image
89
joinedAt: string;
910
lang: string;
1011
theme: string;
@@ -51,6 +52,7 @@ export function AuthProvider({ children }: {children: React.ReactNode;}) {
5152
userData = {
5253
username,
5354
displayName: username,
55+
avatar: undefined,
5456
joinedAt: new Date().toISOString(),
5557
lang: 'en',
5658
theme: 'light'

src/lib/github.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,11 +44,12 @@ export async function putFile(
4444
path: string,
4545
content: string,
4646
message: string,
47+
isBase64: boolean = false,
4748
sha?: string)
4849
{
4950
const body: any = {
5051
message,
51-
content: utf8_to_b64(content),
52+
content: isBase64 ? content : utf8_to_b64(content),
5253
branch
5354
};
5455
if (sha) body.sha = sha;

src/lib/i18n.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,10 @@ const translations = {
3030
joined: 'Joined',
3131
home: 'Home',
3232
members: 'Members',
33-
postReply: 'Post Reply'
33+
postReply: 'Post Reply',
34+
profile: 'Profile',
35+
edit: 'Edit',
36+
save: 'Save'
3437
},
3538
ru: {
3639
login: 'Войти',
@@ -60,7 +63,10 @@ const translations = {
6063
joined: 'Присоединился',
6164
home: 'Главная',
6265
members: 'Участники',
63-
postReply: 'Отправить'
66+
postReply: 'Отправить',
67+
profile: 'Профиль',
68+
edit: 'Изменить',
69+
save: 'Сохранить'
6470
}
6571
};
6672

0 commit comments

Comments
 (0)