refactor: remove LoginService and FirebaseService, add PasswordVisibilityService and integrate
This commit is contained in:
parent
c2c5bd850e
commit
99b1e14034
11 changed files with 60 additions and 749 deletions
|
|
@ -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 {
|
||||
<img
|
||||
class="passwordEye"
|
||||
(click)="loginService.togglePasswordVisibility()"
|
||||
[src]="loginService.passwordIcon"
|
||||
(click)="pwVisibility.togglePasswordVisibility()"
|
||||
[src]="pwVisibility.passwordIcon"
|
||||
/>
|
||||
}
|
||||
<div class="error-msg">
|
||||
|
|
@ -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 {
|
||||
<img
|
||||
class="passwordEyeConfirm"
|
||||
(click)="loginService.togglePasswordVisibility()"
|
||||
[src]="loginService.passwordIcon"
|
||||
(click)="pwVisibility.togglePasswordVisibility()"
|
||||
[src]="pwVisibility.passwordIcon"
|
||||
/>
|
||||
}
|
||||
<div class="error-msg">
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -35,9 +35,6 @@
|
|||
<div class="error-msg">
|
||||
@if (!mail.valid && mail.touched && !isButtonDisabled()) {
|
||||
<p>{{ "login.errorMail0" | translate }}</p>
|
||||
} @if (loginService.errorCode == 'auth/invalid-email' &&
|
||||
!isButtonDisabled()) {
|
||||
<p>{{ "login.errorMail1" | translate }}</p>
|
||||
} @if (mail.valid && mail.touched &&
|
||||
!isEmailValid(loginData.email.toLowerCase()) && !isButtonDisabled()) {
|
||||
<p>{{ "login.errorMail2" | translate }}</p>
|
||||
|
|
@ -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 @@
|
|||
<img
|
||||
class="passwordEye"
|
||||
*ngIf="isPasswordIconVisible"
|
||||
(click)="loginService.togglePasswordVisibility()"
|
||||
[src]="loginService.passwordIcon"
|
||||
(click)="pwVisibility.togglePasswordVisibility()"
|
||||
[src]="pwVisibility.passwordIcon"
|
||||
/>
|
||||
}
|
||||
<div class="error-msg">
|
||||
@if (!password.valid && password.touched && !isButtonDisabled()) {
|
||||
<p>{{ "login.errorPassword0" | translate }}</p>
|
||||
} @if (loginService.errorCode == 'auth/invalid-credential' &&
|
||||
!isButtonDisabled()) {
|
||||
<p>{{ "login.errorPassword1" | translate }}</p>
|
||||
}
|
||||
</div>
|
||||
<div class="checkbox">
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
<img
|
||||
class="passwordEye"
|
||||
(click)="loginService.togglePasswordVisibility()"
|
||||
[src]="loginService.passwordIcon"
|
||||
(click)="pwVisibility.togglePasswordVisibility()"
|
||||
[src]="pwVisibility.passwordIcon"
|
||||
/>
|
||||
}
|
||||
<div class="error-msg">
|
||||
|
|
@ -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 {
|
||||
<img
|
||||
class="passwordEyeConfirm"
|
||||
(click)="loginService.togglePasswordVisibility()"
|
||||
[src]="loginService.passwordIcon"
|
||||
(click)="pwVisibility.togglePasswordVisibility()"
|
||||
[src]="pwVisibility.passwordIcon"
|
||||
/>
|
||||
}
|
||||
<div class="error-msg">
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -18,6 +18,9 @@ import { ToastNotificationService } from './toast-notification.service';
|
|||
export class AuthService {
|
||||
private currentUserIdSubject = new BehaviorSubject<string | null>(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<string | null> {
|
||||
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';
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<any> {
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
|
@ -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<boolean> {
|
||||
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<void>}
|
||||
* @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();
|
||||
});
|
||||
}
|
||||
}
|
||||
22
src/app/services/password-visibility.service.ts
Normal file
22
src/app/services/password-visibility.service.ts
Normal file
|
|
@ -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';
|
||||
}
|
||||
}
|
||||
|
|
@ -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({
|
||||
|
|
|
|||
Loading…
Reference in a new issue