From 634e156f8ef79d0458e810cf7125cb8c8f288969 Mon Sep 17 00:00:00 2001 From: alexander Date: Fri, 15 Nov 2024 12:36:16 +0300 Subject: [PATCH 1/3] Adding an avatar by an authorized user --- backend/package.json | 1 + backend/src/entities/user-settings.entity.ts | 3 + backend/src/main.ts | 2 + .../1730013613468-backend_user_settings.ts | 5 + .../src/users/dto/update-user-settings.dto.ts | 9 +- backend/src/users/users.service.ts | 5 + frontend/package.json | 1 + .../src/components/Forms/AvatarChangeForm.jsx | 14 +- .../src/components/Modals/ChangeAvatar.jsx | 107 +- .../src/components/Modals/RemoveAvatar.jsx | 24 +- .../src/components/Navigation/UserMenu.jsx | 13 +- frontend/src/pages/profile/index.jsx | 5 + frontend/src/slices/index.js | 5 + frontend/src/slices/userSettingsSlice.js | 55 + yarn.lock | 1684 +++++++++++------ 15 files changed, 1322 insertions(+), 611 deletions(-) mode change 100644 => 100755 backend/src/entities/user-settings.entity.ts mode change 100644 => 100755 backend/src/main.ts mode change 100644 => 100755 backend/src/migrations/1730013613468-backend_user_settings.ts mode change 100644 => 100755 backend/src/users/dto/update-user-settings.dto.ts mode change 100644 => 100755 backend/src/users/users.service.ts mode change 100644 => 100755 frontend/src/components/Forms/AvatarChangeForm.jsx mode change 100644 => 100755 frontend/src/components/Modals/ChangeAvatar.jsx mode change 100644 => 100755 frontend/src/components/Modals/RemoveAvatar.jsx mode change 100644 => 100755 frontend/src/components/Navigation/UserMenu.jsx mode change 100644 => 100755 frontend/src/pages/profile/index.jsx mode change 100644 => 100755 frontend/src/slices/index.js create mode 100755 frontend/src/slices/userSettingsSlice.js diff --git a/backend/package.json b/backend/package.json index d9bacb24..24a90817 100644 --- a/backend/package.json +++ b/backend/package.json @@ -45,6 +45,7 @@ "@types/bcrypt": "^5.0.0", "axios": "^1.6.0", "bcrypt": "^5.1.0", + "body-parser": "^1.20.3", "class-transformer": "^0.5.1", "class-validator": "^0.14.0", "cookie-parser": "^1.4.6", diff --git a/backend/src/entities/user-settings.entity.ts b/backend/src/entities/user-settings.entity.ts old mode 100644 new mode 100755 index dc3b2f3f..be2d5879 --- a/backend/src/entities/user-settings.entity.ts +++ b/backend/src/entities/user-settings.entity.ts @@ -19,6 +19,9 @@ export class UserSettings { @Column('text', { default: 'system' }) theme: string; + + @Column({ nullable: true }) + avatar_img: string; @CreateDateColumn() created_at: string; diff --git a/backend/src/main.ts b/backend/src/main.ts old mode 100644 new mode 100755 index 9d90b50a..bd062d52 --- a/backend/src/main.ts +++ b/backend/src/main.ts @@ -1,6 +1,7 @@ /* eslint-disable import/no-import-module-exports */ import { HttpAdapterHost, NestFactory } from '@nestjs/core'; import { NestExpressApplication } from '@nestjs/platform-express'; +import { json } from 'body-parser'; import * as cookieParser from 'cookie-parser'; import { useContainer } from 'class-validator'; import { ValidationPipe } from '@nestjs/common'; @@ -27,6 +28,7 @@ async function bootstrap() { app.setGlobalPrefix('api'); app.enable('trust proxy', ['loopback', 'linklocal', 'uniquelocal']); app.use(cookieParser()); + app.use(json({ limit: '500kb' })); app.useGlobalPipes(new ValidationPipe()); const config = new DocumentBuilder() diff --git a/backend/src/migrations/1730013613468-backend_user_settings.ts b/backend/src/migrations/1730013613468-backend_user_settings.ts old mode 100644 new mode 100755 index 6e86228d..d905a737 --- a/backend/src/migrations/1730013613468-backend_user_settings.ts +++ b/backend/src/migrations/1730013613468-backend_user_settings.ts @@ -26,6 +26,11 @@ export class BackendUserSettings1730013613468 implements MigrationInterface { type: 'varchar(50)', isNullable: false, }, + { + name: 'avatar_img', + type: 'text', + isNullable: true, + }, { name: 'created_at', type: 'timestamp', diff --git a/backend/src/users/dto/update-user-settings.dto.ts b/backend/src/users/dto/update-user-settings.dto.ts old mode 100644 new mode 100755 index ef80cfca..e7b63cb1 --- a/backend/src/users/dto/update-user-settings.dto.ts +++ b/backend/src/users/dto/update-user-settings.dto.ts @@ -1,12 +1,19 @@ -import { IsString } from 'class-validator'; +import { IsString, IsOptional } from 'class-validator'; import { ApiProperty } from '@nestjs/swagger'; export class UpdateUserSettingsDto { @ApiProperty() + @IsOptional() @IsString() theme?: string; @ApiProperty() + @IsOptional() @IsString() language?: string; + + @ApiProperty() + @IsOptional() + @IsString() + avatar_img?: string; } diff --git a/backend/src/users/users.service.ts b/backend/src/users/users.service.ts old mode 100644 new mode 100755 index b6a097f4..92aa42df --- a/backend/src/users/users.service.ts +++ b/backend/src/users/users.service.ts @@ -57,6 +57,7 @@ export class UsersService { userId: newUser.id, theme: 'system', language: 'ru', + avatar_img: null, }); await this.userSettingsRepository.save(userSettings); return newUser; @@ -75,6 +76,7 @@ export class UsersService { ...updatedUser, language: settings.language, theme: settings.theme, + avatar_img: settings.avatar_img, }; } @@ -96,6 +98,7 @@ export class UsersService { ...currentUser, language: updateSettings.language, theme: updateSettings.theme, + avatar_img: updateSettings.avatar_img, }; } @@ -202,6 +205,7 @@ export class UsersService { userId: id, theme: 'system', language: 'ru', + avatar_img: null, }); await this.userSettingsRepository.save(createSettingsUser); } @@ -212,6 +216,7 @@ export class UsersService { ...currentUser, language: settings.language, theme: settings.theme, + avatar_img: settings.avatar_img, }; return { currentUser: data, snippets }; } diff --git a/frontend/package.json b/frontend/package.json index ce0e26a7..b8e0c914 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -24,6 +24,7 @@ "react-bootstrap-typeahead": "^6.3.1", "react-dom": "^18.2.0", "react-i18next": "^13.0.3", + "react-image-file-resizer": "^0.4.8", "react-redux": "^8.1.2", "react-resizable-panels": "^0.0.54", "react-router": "^6.14.2", diff --git a/frontend/src/components/Forms/AvatarChangeForm.jsx b/frontend/src/components/Forms/AvatarChangeForm.jsx old mode 100644 new mode 100755 index 9f6d2103..dea16849 --- a/frontend/src/components/Forms/AvatarChangeForm.jsx +++ b/frontend/src/components/Forms/AvatarChangeForm.jsx @@ -13,6 +13,7 @@ function AvatarChangeForm() { keyPrefix: 'profileSettings', }); const dispatch = useDispatch(); + const avatar = useSelector((state) => state.userSettings.avatar); const username = useSelector((state) => state.user.userInfo.username); const handleEditAvatar = (type) => () => { @@ -25,7 +26,17 @@ function AvatarChangeForm() { className="img-thumbnail rounded-circle overflow-hidden" style={{ width: '14rem', height: '14rem' }} > - + {avatar ? ( + + ) : ( + + )}
+

+ Формат: jpg, png, bmp +

-
diff --git a/frontend/src/components/Modals/RemoveAvatar.jsx b/frontend/src/components/Modals/RemoveAvatar.jsx old mode 100644 new mode 100755 index 92340005..f5f6ec60 --- a/frontend/src/components/Modals/RemoveAvatar.jsx +++ b/frontend/src/components/Modals/RemoveAvatar.jsx @@ -1,10 +1,27 @@ import { Button, Modal, FormGroup } from 'react-bootstrap'; import { useTranslation } from 'react-i18next'; +import { toast } from 'react-toastify'; +import { useDispatch, useSelector } from 'react-redux'; +import { updateUserSettings } from '../../slices/userSettingsSlice'; function RemoveAvatar({ handleClose, isOpen }) { const { t: tMRA } = useTranslation('translation', { keyPrefix: 'modals.removeAvatar', }); + const dispatch = useDispatch(); + const { id } = useSelector((state) => state.user.userInfo); + const { loadingStatus } = useSelector((state) => state.userSettings); + + const handleDeleteAvatar = async () => { + const data = { avatar_img: null }; + dispatch(updateUserSettings({ id, data })).then((req) => { + if (!req.error) { + handleClose(); + } else { + toast.error('Ошибка сети'); + } + }); + }; return ( @@ -13,7 +30,12 @@ function RemoveAvatar({ handleClose, isOpen }) {

{tMRA('message')}

-