Skip to content

Commit

Permalink
add profile, tabs, change info, change password, many many functional
Browse files Browse the repository at this point in the history
  • Loading branch information
Impeqq committed Nov 14, 2021
1 parent 59f60dc commit c5ad851
Show file tree
Hide file tree
Showing 55 changed files with 868 additions and 107 deletions.
6 changes: 6 additions & 0 deletions backend/src/file/file.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,12 @@ export class FileService {
return newFile;
}

async deleteDatabaseFile(id: string) {
const file = await this.fileRepository.findOne({ id });

await this.fileRepository.remove(file);
}

async getFileById(fileId: string) {
const file = await this.fileRepository.findOne(fileId);
if (!file) {
Expand Down
14 changes: 14 additions & 0 deletions backend/src/graphql.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,18 @@ export interface SendMessageInput {
message: string;
}

export interface UpdateInput {
email: string;
firstName: string;
lastName: string;
}

export interface UpdatePasswordInput {
password: string;
newPassword: string;
repeatedPassword: string;
}

export interface SignUpInput {
email: string;
password: string;
Expand Down Expand Up @@ -42,6 +54,8 @@ export interface IMutation {
createChat(user_to?: string): Chat | Promise<Chat>;
setMessagesRead(message_ids?: string[], chat_id?: string): boolean | Promise<boolean>;
sendMessage(input?: SendMessageInput): Message | Promise<Message>;
updatePassword(input?: UpdatePasswordInput): string | Promise<string>;
updateUser(input?: UpdateInput, file?: Upload): string | Promise<string>;
signUp(input?: SignUpInput, file?: Upload): string | Promise<string>;
signIn(input?: SignInInput): string | Promise<string>;
}
Expand Down
3 changes: 1 addition & 2 deletions backend/src/message/message.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,7 @@ export class MessageService {
async setMessagesRead(message_ids: string[]) {
await this.messageRepo
.createQueryBuilder('message')
.update(MessageEntity)
.set({ read: true })
.update(MessageEntity, { read: true })
.where('id IN (:...id)', { id: message_ids })
.execute();
}
Expand Down
25 changes: 25 additions & 0 deletions backend/src/user/dto/update.inputs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { Field, InputType } from '@nestjs/graphql';

@InputType()
export class UpdateInput {
@Field()
email: string;

@Field()
firstName: string;

@Field()
lastName: string;
}

@InputType()
export class UpdatePasswordInput {
@Field()
password: string;

@Field()
newPassword: string;

@Field()
repeatedPassword: string;
}
14 changes: 14 additions & 0 deletions backend/src/user/user.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,24 @@ type Query {
}

type Mutation {
updatePassword(input: UpdatePasswordInput): String
updateUser(input: UpdateInput, file: Upload): String
signUp(input: SignUpInput, file: Upload): String
signIn(input: SignInInput): String
}

input UpdateInput {
email: String!
firstName: String!
lastName: String!
}

input UpdatePasswordInput {
password: String!
newPassword: String!
repeatedPassword: String!
}

input SignUpInput {
email: String!
password: String!
Expand Down
36 changes: 28 additions & 8 deletions backend/src/user/user.resolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import { AuthGuard } from '../guards/auth.guard';
import { UserEntity } from './user.entity';
import { CurrentUser } from './user.decorator';
import { pubsub } from '../lib/pubsub';
import { FileUpload } from 'graphql-upload';
import { UpdateInput, UpdatePasswordInput } from './dto/update.inputs';

@Resolver('User')
export class UserResolver {
Expand All @@ -39,19 +41,13 @@ export class UserResolver {

@Mutation()
async signIn(@Args('input') signInInput: SignInInput) {
const user = await this.userService.signIn({ ...signInInput });

if (user) {
return this.userService.createToken(user);
} else {
throw new UnprocessableEntityException('Invalid credentials 🤥');
}
return await this.userService.signIn({ ...signInInput });
}

@Mutation()
async signUp(
@Args('input') signUpInput: SignUpInput,
@Args('file') file: any,
@Args('file') file: { file: FileUpload },
) {
const _file = await file;
const user = await this.userService.createUser(
Expand All @@ -62,6 +58,30 @@ export class UserResolver {
return this.userService.createToken(user);
}

@Mutation()
@UseGuards(new AuthGuard())
async updateUser(
@Args('input') input: UpdateInput,
@Args('file') file: { file: FileUpload },
@CurrentUser() user: UserEntity,
) {
const _file = await file;
return await this.userService.updateUser(
user.id,
{ ...input },
_file?.file,
);
}

@Mutation()
@UseGuards(new AuthGuard())
async updatePassword(
@Args('input') input: UpdatePasswordInput,
@CurrentUser() user: UserEntity,
) {
return await this.userService.updatePassword(user.id, { ...input });
}

@Subscription(() => UserEntity)
userRegistred() {
return pubsub.asyncIterator('userRegistred');
Expand Down
63 changes: 58 additions & 5 deletions backend/src/user/user.service.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
import { BadRequestException, Injectable } from '@nestjs/common';
import {
BadRequestException,
Injectable,
UnprocessableEntityException,
} from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { UserEntity } from './user.entity';
import { ILike, Repository } from 'typeorm';
Expand All @@ -7,6 +11,7 @@ import * as bcrypt from 'bcryptjs';
import { SignInInput, SignUpInput } from './dto/auth.inputs';
import { FileService } from 'src/file/file.service';
import { FileUpload } from 'graphql-upload';
import { UpdateInput, UpdatePasswordInput } from './dto/update.inputs';

@Injectable()
export class UserService {
Expand Down Expand Up @@ -49,7 +54,7 @@ export class UserService {
const avatar = await this.fileService.uploadDatabaseFile(file);

return this.userRepo
.create({ ...user, password, avatar })
.create({ ...user, email: user.email.toLowerCase(), password, avatar })
.save()
.catch((e) => {
if (/(email)[\s\S]+(already exists)/.test(e.detail)) {
Expand All @@ -61,16 +66,64 @@ export class UserService {
});
}

async updateUser(user_id: string, input: UpdateInput, file: FileUpload) {
const avatar = file && (await this.fileService.uploadDatabaseFile(file));
let oldUser;
if (avatar) {
oldUser = await this.userRepo.findOne(user_id, {
relations: ['avatar'],
});
}

await this.userRepo
.createQueryBuilder('user')
.update(UserEntity, avatar ? { ...input, avatar } : { ...input })
.where('id = :id', { id: user_id })
.execute();

if (avatar) {
await this.fileService.deleteDatabaseFile(oldUser.avatar.id);
}

const user = await this.userRepo.findOne(user_id, {
relations: ['avatar'],
});
return await this.createToken(user);
}

async updatePassword(
user_id: string,
{ password, newPassword, repeatedPassword }: UpdatePasswordInput,
) {
const user = await this.userRepo.findOne({ id: user_id });
const isCorrectPassword = await bcrypt.compare(password, user.password);
if (!isCorrectPassword)
throw new UnprocessableEntityException('Invalid current password 🤥');
if (newPassword !== repeatedPassword)
throw new UnprocessableEntityException(
'Check if the input is correct 🤥',
);

await this.userRepo
.createQueryBuilder('user')
.update(UserEntity, { password: await bcrypt.hash(newPassword, 10) })
.where('id = :id', { id: user_id })
.execute();

return true;
}

async signIn({ email, password }: SignInInput) {
const user = await this.userRepo.findOne({
relations: ['avatar'],
where: { email: email.toLowerCase() },
});
console.log(user);
if (!user) return false;
if (!user) throw new UnprocessableEntityException('Invalid credentials 🤥');
const isCorrectPassword = await bcrypt.compare(password, user.password);
if (!isCorrectPassword)
throw new UnprocessableEntityException('Invalid credentials 🤥');

return isCorrectPassword ? user : false;
return this.createToken(user);
}

async newUsers() {
Expand Down
3 changes: 3 additions & 0 deletions frontend/src/assets/svg/eye-hidden.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions frontend/src/assets/svg/eye.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions frontend/src/assets/svg/upload.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 3 additions & 1 deletion frontend/src/components/sidebar/chats-list/chats-list.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,9 @@ export const ChatsList = () => {
)}
{chats?.map((chat: TChat) => {
const data = {
user: chat.users.filter((user: TUser) => user.id !== me?.id)[0],
user: chat.users.filter(
(user: Omit<TUser, "email">) => user.id !== me?.id
)[0],
date: chat.messages[0]?.createdAt,
message: chat.messages[0]?.message,
isMessageFromMe: chat.messages[0]?.user_from.id === me?.id,
Expand Down
1 change: 1 addition & 0 deletions frontend/src/features/constants/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export const AUTH_TOKEN = "auth-token";
export const API = "localhost:5000";
export const ALERT_SECONDS = 5;
4 changes: 4 additions & 0 deletions frontend/src/features/enum.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export enum AlertTypes {
SUCCESS = "success",
DANGER = "danger",
}
4 changes: 4 additions & 0 deletions frontend/src/features/helpers/getImage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { API } from "@features/constants";

export const getImage = (id?: string) =>
id ? `http://${API}/file/${id}` : null;
3 changes: 2 additions & 1 deletion frontend/src/features/models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@ export const loginModel = {
export const registerModel = {
email: "email",
password: "password",
newPassword: "newPassword",
repeatedPassword: "repeatedPassword",
firstName: "firstName",
lastName: "lastName",
avatar: "avatar",
description: "description",
};
1 change: 1 addition & 0 deletions frontend/src/features/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ export type TUser = {
id: string;
firstName: string;
lastName: string;
email: string;
message?: string;
avatar?: {
filename: string;
Expand Down
14 changes: 10 additions & 4 deletions frontend/src/pages/Auth/login-block/login-block.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import { BaseForm } from "@components";
import { Button, ButtonType, ErrorMessage, Input } from "@ui";
import { AlertMessage, Button, ButtonType, Input } from "@ui";
import styles from "./styles.scss";
import { Link, useHistory } from "react-router-dom";
import { useLazyQuery, useMutation } from "@apollo/client";
import { useMutation } from "@apollo/client";
import { SEND_LOGIN } from "@schemas";
import { useForm, UseFormOptions } from "react-hook-form";
import { loginModel } from "@features/models";
import { AUTH_TOKEN } from "@features/constants";
import { routePath } from "@pages/routes";
import { useLocalStorage } from "@features/hooks";
import { AlertTypes } from "@features/enum";

export default function LoginBlock() {
const history = useHistory();
Expand Down Expand Up @@ -48,9 +49,14 @@ export default function LoginBlock() {
name={loginModel.password}
type={"password"}
/>
<ErrorMessage className={styles.errorMessage} message={error?.message} />
<AlertMessage type={AlertTypes.DANGER} message={error?.message} />
<div className={styles.buttonGroup}>
<Button text={"Sign In"} type={ButtonType.SUBMIT} loading={loading} />
<Button
text={"Sign In"}
type={ButtonType.SUBMIT}
loading={loading}
withDisable={true}
/>
<Link to={"passwordnepomny"}>Forgot your password?</Link>
</div>
</BaseForm>
Expand Down
14 changes: 10 additions & 4 deletions frontend/src/pages/Auth/register-block/register-block.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import {
Button,
ButtonStyle,
ButtonType,
ErrorMessage,
AlertMessage,
IconDirection,
Input,
InputGroup,
Expand All @@ -17,15 +17,17 @@ import { useMutation } from "@apollo/client";
import { AUTH_TOKEN } from "@features/constants";
import { routePath } from "@pages/routes";
import { useHistory } from "react-router-dom";
import { useLocalStorage } from "@features/hooks";
import { AlertTypes } from "@features/enum";

export const RegisterBlock = () => {
const history = useHistory();

const { setItem } = useLocalStorage();
const [sendRegister, { loading, error }] = useMutation(SEND_REGISTER, {
notifyOnNetworkStatusChange: true,
fetchPolicy: "network-only",
onCompleted: ({ signUp }) => {
localStorage.setItem(AUTH_TOKEN, signUp);
setItem(AUTH_TOKEN, signUp);
history.push(routePath.main.path);
},
});
Expand Down Expand Up @@ -68,7 +70,11 @@ export const RegisterBlock = () => {
name={registerModel.password}
type={"password"}
/>
<ErrorMessage className={styles.errorMessage} message={error?.message} />
<AlertMessage
type={AlertTypes.DANGER}
className={styles.errorMessage}
message={error?.message}
/>
<InputGroup>
<Button text={"Sign Up"} type={ButtonType.SUBMIT} loading={loading} />
<Button
Expand Down
Loading

0 comments on commit c5ad851

Please sign in to comment.