From 99b1e14034f9c1b1db81fcd23065f99917517abe Mon Sep 17 00:00:00 2001 From: Chneemann Date: Tue, 8 Apr 2025 08:22:42 +0200 Subject: [PATCH] refactor: remove LoginService and FirebaseService, add PasswordVisibilityService and integrate --- .../pw-reset/pw-reset.component.html | 12 +- .../forgot-pw/pw-reset/pw-reset.component.ts | 4 +- src/app/components/login/login.component.html | 12 +- src/app/components/login/login.component.ts | 7 +- .../login/register/register.component.html | 12 +- .../login/register/register.component.ts | 4 +- src/app/services/auth.service.ts | 16 + src/app/services/firebase.service.ts | 358 ----------------- src/app/services/login.service.ts | 361 ------------------ .../services/password-visibility.service.ts | 22 ++ .../header/navbar/navbar.component.ts | 1 - 11 files changed, 60 insertions(+), 749 deletions(-) delete mode 100644 src/app/services/firebase.service.ts delete mode 100644 src/app/services/login.service.ts create mode 100644 src/app/services/password-visibility.service.ts diff --git a/src/app/components/login/forgot-pw/pw-reset/pw-reset.component.html b/src/app/components/login/forgot-pw/pw-reset/pw-reset.component.html index c1037d3..46e5d9b 100644 --- a/src/app/components/login/forgot-pw/pw-reset/pw-reset.component.html +++ b/src/app/components/login/forgot-pw/pw-reset/pw-reset.component.html @@ -19,7 +19,7 @@ id="password" name="password" #password="ngModel" - [type]="loginService.passwordFieldType" + [type]="pwVisibility.passwordFieldType" placeholder="{{ 'register.password' | translate }}" class="custom-input" autocomplete="new-password" @@ -36,8 +36,8 @@ } @else { }
@@ -52,7 +52,7 @@ id="passwordConfirm" name="passwordConfirm" #passwordConfirm="ngModel" - [type]="loginService.passwordFieldType" + [type]="pwVisibility.passwordFieldType" placeholder="{{ 'register.passwordConfirm' | translate }}" class="custom-input" autocomplete="new-password" @@ -69,8 +69,8 @@ } @else { }
diff --git a/src/app/components/login/forgot-pw/pw-reset/pw-reset.component.ts b/src/app/components/login/forgot-pw/pw-reset/pw-reset.component.ts index 2d0578e..91812e1 100644 --- a/src/app/components/login/forgot-pw/pw-reset/pw-reset.component.ts +++ b/src/app/components/login/forgot-pw/pw-reset/pw-reset.component.ts @@ -9,7 +9,7 @@ import { ActivatedRoute } from '@angular/router'; import { Subscription } from 'rxjs'; import { ButtonStateService } from '../../../../services/button-state.service'; import { AuthService } from '../../../../services/auth.service'; -import { LoginService } from '../../../../services/login.service'; +import { PasswordVisibilityService } from '../../../../services/password-visibility.service'; @Component({ selector: 'app-pw-reset', @@ -38,7 +38,7 @@ export class PwResetComponent { }; constructor( - public loginService: LoginService, + public pwVisibility: PasswordVisibilityService, private authService: AuthService, private buttonStateService: ButtonStateService, private route: ActivatedRoute diff --git a/src/app/components/login/login.component.html b/src/app/components/login/login.component.html index 279d2ef..a58c9eb 100644 --- a/src/app/components/login/login.component.html +++ b/src/app/components/login/login.component.html @@ -35,9 +35,6 @@
@if (!mail.valid && mail.touched && !isButtonDisabled()) {

{{ "login.errorMail0" | translate }}

- } @if (loginService.errorCode == 'auth/invalid-email' && - !isButtonDisabled()) { -

{{ "login.errorMail1" | translate }}

} @if (mail.valid && mail.touched && !isEmailValid(loginData.email.toLowerCase()) && !isButtonDisabled()) {

{{ "login.errorMail2" | translate }}

@@ -48,7 +45,7 @@ id="password" name="password" #password="ngModel" - [type]="loginService.passwordFieldType" + [type]="pwVisibility.passwordFieldType" placeholder="{{ 'login.password' | translate }}" class="custom-input" autocomplete="current-password" @@ -67,16 +64,13 @@ }
@if (!password.valid && password.touched && !isButtonDisabled()) {

{{ "login.errorPassword0" | translate }}

- } @if (loginService.errorCode == 'auth/invalid-credential' && - !isButtonDisabled()) { -

{{ "login.errorPassword1" | translate }}

}
diff --git a/src/app/components/login/login.component.ts b/src/app/components/login/login.component.ts index 2ff5b29..6691318 100644 --- a/src/app/components/login/login.component.ts +++ b/src/app/components/login/login.component.ts @@ -3,7 +3,6 @@ import { Component } from '@angular/core'; import { FormsModule, NgForm } from '@angular/forms'; import { FormBtnComponent } from '../../shared/components/buttons/form-btn/form-btn.component'; import { ActivatedRoute, Router, RouterLink } from '@angular/router'; -import { LoginService } from '../../services/login.service'; import { FooterComponent } from './footer/footer.component'; import { HeaderComponent } from './header/header.component'; import { TranslateModule } from '@ngx-translate/core'; @@ -12,6 +11,7 @@ import { OverlayService } from '../../services/overlay.service'; import { AuthService } from '../../services/auth.service'; import { TokenService } from '../../services/token.service'; import { ButtonStateService } from '../../services/button-state.service'; +import { PasswordVisibilityService } from '../../services/password-visibility.service'; @Component({ selector: 'app-login', @@ -39,13 +39,12 @@ export class LoginComponent { }; constructor( + public pwVisibility: PasswordVisibilityService, private authService: AuthService, - public loginService: LoginService, private tokenService: TokenService, private overlayService: OverlayService, private buttonStateService: ButtonStateService, - private route: ActivatedRoute, - private router: Router + private route: ActivatedRoute ) {} ngOnInit() { diff --git a/src/app/components/login/register/register.component.html b/src/app/components/login/register/register.component.html index 9a0d55f..38270e7 100644 --- a/src/app/components/login/register/register.component.html +++ b/src/app/components/login/register/register.component.html @@ -85,7 +85,7 @@ id="password" name="password" #password="ngModel" - [type]="loginService.passwordFieldType" + [type]="pwVisibility.passwordFieldType" placeholder="{{ 'register.password' | translate }}" class="custom-input" autocomplete="new-password" @@ -102,8 +102,8 @@ } @else { }
@@ -119,7 +119,7 @@ id="passwordConfirm" name="passwordConfirm" #passwordConfirm="ngModel" - [type]="loginService.passwordFieldType" + [type]="pwVisibility.passwordFieldType" placeholder="{{ 'register.passwordConfirm' | translate }}" class="custom-input" autocomplete="new-password" @@ -136,8 +136,8 @@ } @else { }
diff --git a/src/app/components/login/register/register.component.ts b/src/app/components/login/register/register.component.ts index 88b2e36..d4bc443 100644 --- a/src/app/components/login/register/register.component.ts +++ b/src/app/components/login/register/register.component.ts @@ -4,7 +4,7 @@ import { FooterComponent } from '../footer/footer.component'; import { FormsModule, NgForm } from '@angular/forms'; import { CommonModule } from '@angular/common'; import { FormBtnComponent } from '../../../shared/components/buttons/form-btn/form-btn.component'; -import { LoginService } from '../../../services/login.service'; +import { PasswordVisibilityService } from '../../../services/password-visibility.service'; import { BtnBackComponent } from '../../../shared/components/buttons/btn-back/btn-back.component'; import { RouterModule } from '@angular/router'; import { TranslateModule, TranslateService } from '@ngx-translate/core'; @@ -43,7 +43,7 @@ export class RegisterComponent { }; constructor( - public loginService: LoginService, + public pwVisibility: PasswordVisibilityService, public translateService: TranslateService, private authService: AuthService, private buttonStateService: ButtonStateService diff --git a/src/app/services/auth.service.ts b/src/app/services/auth.service.ts index 353400b..cd3c68a 100755 --- a/src/app/services/auth.service.ts +++ b/src/app/services/auth.service.ts @@ -18,6 +18,9 @@ import { ToastNotificationService } from './toast-notification.service'; export class AuthService { private currentUserIdSubject = new BehaviorSubject(null); + passwordFieldType: string = 'password'; + passwordIcon: string = './../../../assets/img/login/close-eye.svg'; + constructor( private apiService: ApiService, private tokenService: TokenService, @@ -112,4 +115,17 @@ export class AuthService { getCurrentUserId(): Observable { return this.currentUserIdSubject.asObservable(); } + + togglePasswordVisibility() { + this.passwordFieldType = + this.passwordFieldType === 'password' ? 'text' : 'password'; + this.toggleIcon(); + } + + toggleIcon() { + this.passwordIcon = + this.passwordIcon === './../../../assets/img/login/close-eye.svg' + ? './../../../assets/img/login/open-eye.svg' + : './../../../assets/img/login/close-eye.svg'; + } } diff --git a/src/app/services/firebase.service.ts b/src/app/services/firebase.service.ts deleted file mode 100644 index 119bb16..0000000 --- a/src/app/services/firebase.service.ts +++ /dev/null @@ -1,358 +0,0 @@ -import { Injectable, OnDestroy, inject } from '@angular/core'; -import { - Firestore, - addDoc, - collection, - deleteDoc, - doc, - onSnapshot, - setDoc, - updateDoc, -} from '@angular/fire/firestore'; -import { Task } from '../interfaces/task.interface'; -import { User } from '../interfaces/user.interface'; -import CryptoES from 'crypto-es'; -import { CryptoESSecretKey } from './../environments/config'; -import { Observable, of } from 'rxjs'; - -@Injectable({ - providedIn: 'root', -}) -export class FirebaseService implements OnDestroy { - firestore: Firestore = inject(Firestore); - private secretKey: string = CryptoESSecretKey.secretKey; - - allTasks: Task[] = []; - filteredTasks: Task[] = []; - allUsers: User[] = []; - - unsubTask; - unsubUser; - - constructor() { - this.unsubTask = this.subTaskList(); - this.unsubUser = this.subUserList(); - } - - // ------------- TASKS ------------- // - - /** - * Subscribes to Firestore for the list of all tasks. - * - * This function uses the `onSnapshot` function from `@angular/fire/firestore` to - * subscribe to the 'tasks' collection in Firestore. When the data in the - * collection changes, this function is called with a `QuerySnapshot` object as an - * argument. The function then iterates over the `QuerySnapshot` and creates a - * `Task` object from each document in the snapshot. The created `Task` objects are - * then stored in the `allTasks` array. - * - * @returns The unsubscribe function from `onSnapshot`. - */ - subTaskList() { - return onSnapshot(collection(this.firestore, 'tasks'), (list) => { - this.allTasks = []; - list.forEach((element) => { - const taskWithId = { id: element.id, ...element.data() } as Task; - this.allTasks.push(taskWithId); - }); - }); - } - - /** - * Returns the list of all tasks. - * - * This function simply returns the value of the `allTasks` property, which is - * an array of `Task` objects that is kept up-to-date by the `subTaskList` - * function. - * - * @returns The list of all tasks. - */ - getAllTasks(): Task[] { - return this.allTasks; - } - - /** - * Returns the list of tasks that match the current filter. - * - * This function simply returns the value of the `filteredTasks` property, which - * is an array of `Task` objects that is kept up-to-date by the `searchTask` - * function. - * - * @returns The list of filtered tasks. - */ - getFiltertTasks(): Task[] { - return this.filteredTasks; - } - - /** - * Updates the status of a task in the Firestore database. - * - * This function updates the status of the task identified by `taskId` - * using the status from the `allTasks` array at the specified `index`. - * - * @param taskId The ID of the task to be updated. - * @param index The index of the task in the `allTasks` array. - */ - async updateTask(taskId: string, index: number) { - await updateDoc(doc(collection(this.firestore, 'tasks'), taskId), { - status: this.allTasks[index].status, - }).catch((err) => { - console.error(err); - }); - } - - /** - * Updates the subtasksDone property of a task in the Firestore database. - * - * This function updates the subtasksDone property of the task identified by - * `taskId` with the `array` parameter. The `array` parameter is an array of booleans - * that indicates which subtasks are completed. - * - * @param taskId The ID of the task to be updated. - * @param array An array of booleans that indicates which subtasks are completed. - */ - async updateSubTask(taskId: string, array: boolean[]) { - await updateDoc(doc(collection(this.firestore, 'tasks'), taskId), { - subtasksDone: array, - }).catch((err) => { - console.error(err); - }); - } - - /** - * Replaces the task identified by `taskId` with the data from the `newData` object. - * - * This function uses the `setDoc` function from `@angular/fire/firestore` to - * replace the task identified by `taskId` with the data from the `newData` object. - * The `newData` object should be of type `Task` and should contain all the fields - * that are required for a task. - * - * @param taskId The ID of the task to be replaced. - * @param newData The new task data. - */ - async replaceTaskData(taskId: string, newData: Task) { - await setDoc( - doc(collection(this.firestore, 'tasks'), taskId), - newData - ).catch((err) => { - console.error(err); - }); - } - - /** - * Deletes the task with the specified ID from the Firestore database. - * - * This function uses the `deleteDoc` function to remove the task identified by - * `taskId` from the 'tasks' collection in Firestore. - * - * @param taskId The ID of the task to be deleted. - */ - async deleteTask(taskId: string) { - await deleteDoc(doc(collection(this.firestore, 'tasks'), taskId)).catch( - (err) => { - console.error(err); - } - ); - } - - /** - * Adds a new task to the Firestore database. - * - * This function uses the `addDoc` function from `@angular/fire/firestore` to add - * the task to the 'tasks' collection in Firestore. The `task` parameter should be - * of type `Task` and should contain all the fields that are required for a task. - * - * @param task The new task data to be added. - */ - async addNewTask(task: Task) { - await addDoc(collection(this.firestore, 'tasks'), task).catch((err) => { - console.error(err); - }); - } - - // ------------- USERS ------------- // - - /** - * Subscribes to Firestore for the list of all users. - * - * This function uses the `onSnapshot` function from `@angular/fire/firestore` to - * subscribe to the 'users' collection in Firestore. When the data in the - * collection changes, this function is called with a `QuerySnapshot` object as an - * argument. The function then iterates over the `QuerySnapshot` and creates a - * `User` object from each document in the snapshot. The created `User` objects are - * then stored in the `allUsers` array. - * - * @returns The unsubscribe function from `onSnapshot`. - */ - subUserList() { - return onSnapshot(collection(this.firestore, 'users'), (list) => { - this.allUsers = []; - list.forEach((element) => { - const userWithId = { id: element.id, ...element.data() } as User; - this.allUsers.push(userWithId); - }); - }); - } - - /** - * Returns the list of all users. - * - * @returns An array of User objects. - */ - getAllUsers(): User[] { - return this.allUsers; - } - - /** - * Retrieves a list of all users excluding those with initials 'G' (typically representing guest users). - * - * @returns An array of User objects without guest users. - */ - getAllUserWithoutGuest(): User[] { - return this.getAllUsers().filter((user) => user.initials !== 'G'); - } - - /** - * Retrieves a list of all users excluding guest users and the current user and the task creator. - * - * @param taskCreator - The id of the task creator. - * @returns An array of User objects excluding guest users, the current user, and the task creator. - */ - getFilteredUsers(taskCreator: string): User[] { - const currentUser = this.getCurrentUserId(); - const filteredUsers = this.getAllUsers().filter( - (user) => user.initials !== 'G' && user.id !== currentUser - ); - return filteredUsers.filter((user) => user.id !== taskCreator); - } - - /** - * Retrieves a list of users from the list of all users that have the given userId. - * - * @param userId - The id of the user to be retrieved. - * @returns An array of User objects with the given userId. - */ - getUserDataFromId(userId: string): User[] { - return this.getAllUsers().filter((user) => user.id === userId); - } - - /** - * Retrieves a list of users from the list of all users that have the given userUid. - * - * @param userUid - The unique identifier of the user to be retrieved. - * @returns An array of User objects with the given userUid. - */ - getUserDataFromUid(userUid: string): User[] { - return this.getAllUsers().filter((user) => user.id === userUid); - } - - /** - * Retrieves the current user ID from local storage. - * - * The current user ID is stored in local storage encrypted with the secret key. - * This method decrypts the value and returns the current user ID as a string. - * If the value does not exist or is not decryptable, it returns null. - * @returns The current user ID as a string, or null if not found. - */ - getCurrentUserId() { - const encryptedValue = localStorage.getItem('currentUserJOIN'); - if (encryptedValue) { - const bytes = CryptoES.AES.decrypt(encryptedValue, this.secretKey); - const decryptedValue = bytes.toString(CryptoES.enc.Utf8); - return JSON.parse(decryptedValue); - } - return null; - } - - /** - * Retrieves specific user information from the list of all users. - * - * @param userId - The id of the user whose information is to be retrieved. - * @param query - The specific field of the user object to be retrieved, such as 'firstName', 'email', etc. - * @returns An array containing the values of the specified field for the user with the given userId. - */ - getUserDetails(userId: string, query: keyof User) { - return this.getAllUsers() - .filter((user) => user.id === userId) - .map((user) => user[query]); - } - - /** - * Deletes a user document from the Firestore database. - * - * @param docId The id of the document to be deleted. - * @returns A Promise resolving to void. - * @throws An error if the deletion attempt fails. - */ - async deleteUser(docId: string) { - await deleteDoc(doc(collection(this.firestore, 'users'), docId)).catch( - (err) => { - console.error(err); - } - ); - } - - /** - * Updates a user document in the Firestore database. - * - * @param docId The id of the document to be updated. - * @param data The object with the new data to be updated. - * @returns A Promise resolving to void. - * @throws An error if the update attempt fails. - */ - async updateUserData(docId: string, data: any) { - await updateDoc( - doc(collection(this.firestore, 'users'), docId), - data - ).catch((err) => { - console.error(err); - }); - } - - /** - * Adds a new user to the Firestore database. - * - * @param userData The data of the user to be added. - * @returns A Promise resolving to the newly added user document. - * @throws An error if the add attempt fails. - */ - async addNewUser(userData: User) { - try { - return await addDoc(collection(this.firestore, 'users'), userData); - } catch (err) { - console.error(err); - throw err; - } - } - - // ------------- AUTH ------------- // - - /** - * Retrieves the current user from local storage. - * - * The current user is stored in local storage encrypted with the secret key. - * This method decrypts the value and returns the current user as an Observable, - * or null if not found. - * @returns An Observable resolving to the current user, or null if not found. - */ - getAuthUser(): Observable { - const encryptedValue = localStorage.getItem('currentUserJOIN'); - if (encryptedValue) { - const bytes = CryptoES.AES.decrypt(encryptedValue, this.secretKey); - const decryptedValue = bytes.toString(CryptoES.enc.Utf8); - return of(JSON.parse(decryptedValue)); - } - return of(null); - } - - // ------------- Destroy ------------- // - - /** - * Lifecycle hook that is called when the component is about to be destroyed. - * Unsubscribes from task and user changes to prevent memory leaks. - */ - ngOnDestroy() { - this.unsubTask(); - this.unsubUser(); - } -} diff --git a/src/app/services/login.service.ts b/src/app/services/login.service.ts deleted file mode 100644 index f862ae8..0000000 --- a/src/app/services/login.service.ts +++ /dev/null @@ -1,361 +0,0 @@ -import { Injectable, inject } from '@angular/core'; -import { - getAuth, - signInWithEmailAndPassword, - signInWithPopup, - GoogleAuthProvider, - createUserWithEmailAndPassword, - sendPasswordResetEmail, - confirmPasswordReset, -} from 'firebase/auth'; -import { FirebaseService } from './firebase.service'; -import { - Firestore, - addDoc, - collection, - doc, - getDocs, - query, - updateDoc, - where, -} from '@angular/fire/firestore'; -import { User } from '../interfaces/user.interface'; -import { Router } from '@angular/router'; -import CryptoES from 'crypto-es'; -import { CryptoESSecretKey } from '../environments/config'; -import { catchError, map, Observable, of } from 'rxjs'; -import { ButtonStateService } from './button-state.service'; - -@Injectable({ - providedIn: 'root', -}) -export class LoginService { - firestore: Firestore = inject(Firestore); - passwordFieldType: string = 'password'; - passwordIcon: string = './../../../assets/img/login/close-eye.svg'; - errorCode: string = ''; - - private secretKey: string = CryptoESSecretKey.secretKey; - - constructor( - private firebaseService: FirebaseService, - private buttonStateService: ButtonStateService, - private router: Router - ) {} - - /** - * Checks if a user is logged in. - * - * @returns An Observable resolving to a boolean indicating whether a user is logged in. - */ - checkAuthUser(): Observable { - return this.firebaseService.getAuthUser().pipe( - map((user) => { - if (user) { - return true; - } else { - return false; - } - }), - catchError(() => { - return of(false); - }) - ); - } - - /** - * Logs in a user given their email and password. - * @param loginData An object with properties `mail` and `password`. - * @returns A Promise resolving to void. - * @throws An error if the login attempt fails. - */ - login(loginData: { mail: string; password: string }) { - signInWithEmailAndPassword(getAuth(), loginData.mail, loginData.password) - .then((userCredential) => { - const user = userCredential.user; - const userData = this.firebaseService.getUserDataFromUid(user.uid); - if (userData.length > 0 && userData[0].id) { - this.getUserIdInLocalStorage(userData[0].id); - this.updateUserOnlineStatus(userData[0].id, true); - } - }) - .catch((error) => { - this.errorCode = error.code; - this.buttonStateService.disableButton(); - }); - } - - /** - * Registers a new user given their name, first name, last name, email and password. - * @param registerData An object with properties `name`, `firstName`, `lastName`, `mail` and `password`. - * @returns A Promise resolving to void. - * @throws An error if the registration attempt fails. - */ - register(registerData: { - name: string; - firstName: string; - lastName: string; - mail: string; - password: string; - }) { - createUserWithEmailAndPassword( - getAuth(), - registerData.mail, - registerData.password - ) - .then((userCredential) => { - const user = userCredential.user; - - const userDataToSave: User = { - email: user.email || '', - firstName: registerData.firstName - ? registerData.firstName.charAt(0).toUpperCase() + - registerData.firstName.slice(1) - : '', - lastName: registerData.lastName - ? registerData.lastName.charAt(0).toUpperCase() + - registerData.lastName.slice(1) - : '', - isContactOnly: false, - isOnline: true, - phone: '', - initials: registerData.name - ? registerData.firstName.slice(0, 1).toUpperCase() + - registerData.lastName.slice(0, 1).toUpperCase() - : '', - color: '', - lastLogin: new Date().getTime(), - }; - this.createUserInFirestore(userDataToSave); - }) - .catch((error) => { - this.buttonStateService.disableButton(); - }); - } - - /** - * Google login method. - * - * Signs in the user with Google Authentication. - * If the user is not in the database, creates a new user with the provided name and email. - * If the user is already in the database, logins the user. - * - * @returns {Promise} - * @memberof LoginService - */ - googleLogin() { - const auth = getAuth(); - const provider = new GoogleAuthProvider(); - - signInWithPopup(auth, provider) - .then((userCredential) => { - const user = userCredential.user; - const usersCollection = collection(this.firestore, 'users'); - const querySnapshot = query( - usersCollection, - where('uId', '==', user.uid) - ); - getDocs(querySnapshot).then((snapshot) => { - if (snapshot.empty) { - const displayName = user.displayName || ''; - const [firstName = '', lastName = ''] = displayName.split(' '); - this.createUserInFirestore({ - email: user.email || 'no mail', - firstName: firstName.charAt(0).toUpperCase() + firstName.slice(1), - lastName: lastName.charAt(0).toUpperCase() + lastName.slice(1), - isContactOnly: false, - isOnline: true, - phone: '', - initials: firstName.slice(0, 1) + lastName.slice(0, 1), - color: '', - lastLogin: 0, - }); - } else { - this.ifExistUser(user.uid); - } - }); - }) - .catch((error) => { - this.buttonStateService.disableButton(); - }); - } - - /** - * Creates a new user in the Firestore database with the given user data. - * If the user already exists in the database, the method does nothing. - * @param user The user data to save. The user data should contain the following properties: `uId`, `email`, `firstName`, `lastName`, `initials`, `color`. - * @returns A Promise resolving to void. - */ - async createUserInFirestore(user: User) { - const userDataToSave: User = { - email: user.email, - firstName: user.firstName || '', - lastName: user.lastName || '', - isContactOnly: false, - isOnline: true, - phone: '', - initials: user.initials, - color: user.color, - lastLogin: new Date().getTime(), - }; - const usersCollection = collection(this.firestore, 'users'); - try { - const docRef = await addDoc(usersCollection, userDataToSave); - //this.ifExistUser(user.uId); - } catch (error) { - console.error(error); - } - } - - /** - * Checks if a user with the given user id exists in the database. - * If the user exists, the method saves the user's id in the local storage and - * updates the user's online status in the database. - * @param user The id of the user to check. - * @returns void - */ - ifExistUser(user: string) { - const userData = this.firebaseService.getUserDataFromUid(user); - if (userData.length > 0 && userData[0].id) { - this.getUserIdInLocalStorage(userData[0].id); - this.updateUserOnlineStatus(userData[0].id, true); - } - } - - /** - * Updates the online status of the user with the given id in the Firestore database. - * If the user's status is set to true, the user's lastLogin property is also updated - * to the current time. - * @param userId The id of the user to update. - * @param status The new online status of the user. - * @returns A Promise resolving to void. - */ - async updateUserOnlineStatus(userId: string, status: boolean) { - await updateDoc(doc(collection(this.firestore, 'users'), userId), { - status: status, - lastLogin: new Date().getTime(), - }); - window.location.reload(); - } - - /** - * Toggles the visibility of the password field. - * - * This function switches the input type between 'password' and 'text', - * allowing the user to view or hide the password as needed. - * It also updates the icon to reflect the current visibility state. - */ - togglePasswordVisibility() { - this.passwordFieldType = - this.passwordFieldType === 'password' ? 'text' : 'password'; - this.toggleIcon(); - } - - /** - * Toggles the password visibility icon. - * - * This function switches the icon src between the 'close eye' and 'open eye' - * icons, indicating whether the password is hidden or visible. - */ - toggleIcon() { - this.passwordIcon = - this.passwordIcon === './../../../assets/img/login/close-eye.svg' - ? './../../../assets/img/login/open-eye.svg' - : './../../../assets/img/login/close-eye.svg'; - } - - /** - * Saves the given userId to local storage, encrypted with the secret key. - * - * The userId is first stringified and then encrypted with the secret key. - * The encrypted string is then saved to local storage under the key - * 'currentUserJOIN'. Additionally, the current timestamp is saved to local - * storage under the key 'sessionTimeJOIN'. - * - * @param userId The id of the user to be saved. - */ - getUserIdInLocalStorage(userId: string): void { - const encryptedValue = CryptoES.AES.encrypt( - JSON.stringify(userId), - this.secretKey - ).toString(); - localStorage.setItem('currentUserJOIN', encryptedValue); - localStorage.setItem('sessionTimeJOIN', new Date().getTime().toString()); - } - - // LOGOUT - - /** - * Logs out the current user by deleting the user ID from local storage and - * updating the user's online status to offline in the Firestore database. - * - * @param userId The id of the user to log out. - */ - logout(userId: string) { - this.deleteUserIdInLocalStorage(); - this.updateUserOnlineStatus(userId, false); - } - - /** - * Deletes the user ID from local storage, effectively logging the user out. - * - * This function removes the user ID from local storage by deleting the - * 'currentUserJOIN' and 'sessionTimeJOIN' items. - */ - deleteUserIdInLocalStorage() { - localStorage.removeItem('currentUserJOIN'); - localStorage.removeItem('sessionTimeJOIN'); - } - - // FORGOT PASSWORD - - /** - * Initiates the password reset process for the given email address. - * - * This function sends a password reset email to the specified email address - * using Firebase Authentication. Upon successful sending of the email, the - * user is redirected to the password reset notice page. If there is an error, - * the button will be re-enabled for the user to try again. - * - * @param email The email address of the user requesting a password reset. - */ - passwordReset(email: string) { - const auth = getAuth(); - const actionCodeSettings = { - url: 'https://join.andre-kempf.com/', - handleCodeInApp: true, - }; - sendPasswordResetEmail(auth, email, actionCodeSettings) - .then(() => { - this.router.navigate(['/login/notice/pw-send']); - this.buttonStateService.disableButton(); - }) - .catch((error) => { - this.buttonStateService.disableButton(); - }); - } - - /** - * Resets the user's password given the new password and the oobCode from the - * password reset email. - * - * This function sends a request to Firebase Authentication to reset the user's - * password. If the request is successful, it navigates to the password reset - * notice page and re-enables the submit button. If there is an error, the - * button will be re-enabled for the user to try again. - * - * @param newPW The new password to be set for the user. - * @param oobCode The one-time code from the password reset email. - */ - newPassword(newPW: string, oobCode: string) { - const auth = getAuth(); - confirmPasswordReset(auth, oobCode, newPW) - .then(() => { - this.router.navigate(['/login/notice/pw-change']); - this.buttonStateService.disableButton(); - }) - .catch((error) => { - this.buttonStateService.disableButton(); - }); - } -} diff --git a/src/app/services/password-visibility.service.ts b/src/app/services/password-visibility.service.ts new file mode 100644 index 0000000..466f31d --- /dev/null +++ b/src/app/services/password-visibility.service.ts @@ -0,0 +1,22 @@ +import { Injectable } from '@angular/core'; + +@Injectable({ + providedIn: 'root', +}) +export class PasswordVisibilityService { + passwordFieldType: string = 'password'; + passwordIcon: string = './../../../assets/img/login/close-eye.svg'; + + togglePasswordVisibility() { + this.passwordFieldType = + this.passwordFieldType === 'password' ? 'text' : 'password'; + this.toggleIcon(); + } + + toggleIcon() { + this.passwordIcon = + this.passwordIcon === './../../../assets/img/login/close-eye.svg' + ? './../../../assets/img/login/open-eye.svg' + : './../../../assets/img/login/close-eye.svg'; + } +} diff --git a/src/app/shared/components/header/navbar/navbar.component.ts b/src/app/shared/components/header/navbar/navbar.component.ts index 1d45b3e..4a6095e 100644 --- a/src/app/shared/components/header/navbar/navbar.component.ts +++ b/src/app/shared/components/header/navbar/navbar.component.ts @@ -3,7 +3,6 @@ import { Router, RouterModule } from '@angular/router'; import { LanguageService } from '../../../../services/language.service'; import { CommonModule } from '@angular/common'; import { TranslateModule } from '@ngx-translate/core'; -import { LoginService } from '../../../../services/login.service'; import { AuthService } from '../../../../services/auth.service'; @Component({