refactor: Refactor SharedService, extract logic into smaller services (ResizeService and ColorService)
This commit is contained in:
parent
a00690ffae
commit
8659c16517
11 changed files with 92 additions and 139 deletions
|
|
@ -227,7 +227,7 @@ export class AddTaskComponent implements OnInit {
|
|||
* @returns The updated search input flag.
|
||||
*/
|
||||
updateSearchInput() {
|
||||
this.searchValue = this.sharedService.replaceXSSChars(this.searchValue);
|
||||
this.searchValue = this.replaceXSSChars(this.searchValue);
|
||||
if (this.searchValue) {
|
||||
this.searchInput = this.searchValue.toLowerCase().length > 0;
|
||||
} else {
|
||||
|
|
@ -241,7 +241,7 @@ export class AddTaskComponent implements OnInit {
|
|||
* This ensures that the subtask value is sanitized before further processing or storage.
|
||||
*/
|
||||
updateSubtaskValue() {
|
||||
this.subtaskValue = this.sharedService.replaceXSSChars(this.subtaskValue);
|
||||
this.subtaskValue = this.replaceXSSChars(this.subtaskValue);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -400,14 +400,10 @@ export class AddTaskComponent implements OnInit {
|
|||
* @returns {void}
|
||||
*/
|
||||
checkCrossSiteScripting() {
|
||||
this.taskData.title = this.sharedService.replaceXSSChars(
|
||||
this.taskData.title
|
||||
);
|
||||
this.taskData.description = this.sharedService.replaceXSSChars(
|
||||
this.taskData.description
|
||||
);
|
||||
this.taskData.title = this.replaceXSSChars(this.taskData.title);
|
||||
this.taskData.description = this.replaceXSSChars(this.taskData.description);
|
||||
this.taskData.subtasksTitle = this.taskData.subtasksTitle.map((title) =>
|
||||
this.sharedService.replaceXSSChars(title)
|
||||
this.replaceXSSChars(title)
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -421,6 +417,10 @@ export class AddTaskComponent implements OnInit {
|
|||
this.closeDialog();
|
||||
}
|
||||
|
||||
replaceXSSChars(input: string) {
|
||||
return input.replace(/</g, '<').replace(/>/g, '>');
|
||||
}
|
||||
|
||||
@HostListener('document:click', ['$event'])
|
||||
/**
|
||||
* Closes the assigned dropdown menu if the user clicks anywhere outside of
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ import { TaskEmptyComponent } from './task/task-empty/task-empty.component';
|
|||
import { FormsModule } from '@angular/forms';
|
||||
import { OverlayService } from '../../services/overlay.service';
|
||||
import { Router } from '@angular/router';
|
||||
import { SharedService } from '../../services/shared.service';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { TaskHighlightedComponent } from './task/task-highlighted/task-highlighted.component';
|
||||
import { ApiService } from '../../services/api.service';
|
||||
|
|
@ -16,6 +15,7 @@ import { LoadingSpinnerComponent } from '../../shared/components/loading-spinner
|
|||
import { finalize, Subject, takeUntil } from 'rxjs';
|
||||
import { TaskUpdateService } from '../../services/task-update.service';
|
||||
import { ToastNotificationService } from '../../services/toast-notification.servic';
|
||||
import { ResizeService } from '../../services/resize.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-board',
|
||||
|
|
@ -43,7 +43,7 @@ export class BoardComponent {
|
|||
constructor(
|
||||
public dragDropService: DragDropService,
|
||||
public overlayService: OverlayService,
|
||||
private sharedService: SharedService,
|
||||
private resizeService: ResizeService,
|
||||
private taskService: TaskService,
|
||||
private apiService: ApiService,
|
||||
private taskUpdateService: TaskUpdateService,
|
||||
|
|
@ -129,7 +129,7 @@ export class BoardComponent {
|
|||
* @param status The status to be used for the new task.
|
||||
*/
|
||||
addNewTaskOverlay(status: string) {
|
||||
this.sharedService.isPageViewMedia
|
||||
this.resizeService.isPageViewMedia
|
||||
? this.router.navigate(['/add-task', status])
|
||||
: this.overlayService.setOverlayData('newTaskOverlay', status);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@
|
|||
>
|
||||
{{ task.category }}
|
||||
</div>
|
||||
@if (sharedService.isPageViewMedia) {
|
||||
@if (resizeService.isPageViewMedia) {
|
||||
<div class="menu-btn" (click)="handleMenuButtonClick($event, task.id)">
|
||||
<img
|
||||
class="menu-img"
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import { FirebaseService } from '../../../services/firebase.service';
|
|||
import { OverlayService } from '../../../services/overlay.service';
|
||||
import { Router } from '@angular/router';
|
||||
import { TaskMenuComponent } from './task-menu/task-menu.component';
|
||||
import { SharedService } from '../../../services/shared.service';
|
||||
import { ResizeService } from '../../../services/resize.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-task',
|
||||
|
|
@ -34,7 +34,7 @@ export class TaskComponent {
|
|||
public dragDropService: DragDropService,
|
||||
public firebaseService: FirebaseService,
|
||||
public overlayService: OverlayService,
|
||||
public sharedService: SharedService,
|
||||
public resizeService: ResizeService,
|
||||
private router: Router
|
||||
) {}
|
||||
|
||||
|
|
@ -74,7 +74,7 @@ export class TaskComponent {
|
|||
if (this.isMenuOpen) {
|
||||
this.toggleTaskMenu();
|
||||
}
|
||||
this.sharedService.isPageViewMedia
|
||||
this.resizeService.isPageViewMedia
|
||||
? this.router.navigate(['/task', taskId])
|
||||
: this.overlayService.setOverlayData('taskOverlay', taskId);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import { TranslateModule } from '@ngx-translate/core';
|
|||
import { ContactFormComponent } from './contact-form/contact-form.component';
|
||||
import { SharedService } from '../../../services/shared.service';
|
||||
import { ColorPickerModule } from 'ngx-color-picker';
|
||||
import { ColorService } from '../../../services/color.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-contact-edit',
|
||||
|
|
@ -30,7 +31,8 @@ export class ContactEditNewComponent implements OnInit {
|
|||
|
||||
constructor(
|
||||
public firebaseService: FirebaseService,
|
||||
public sharedService: SharedService
|
||||
public sharedService: SharedService,
|
||||
private colorService: ColorService
|
||||
) {}
|
||||
|
||||
/**
|
||||
|
|
@ -39,7 +41,7 @@ export class ContactEditNewComponent implements OnInit {
|
|||
* This method sets a random color for the user to be edited.
|
||||
*/
|
||||
ngOnInit() {
|
||||
this.randomColor = this.sharedService.generateRandomColor();
|
||||
this.randomColor = this.colorService.generateRandomColor();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@
|
|||
autocomplete="off"
|
||||
required
|
||||
/>
|
||||
@if (sharedService.isContactViewMedia) {
|
||||
@if (resizeService.isContactViewMedia) {
|
||||
<div class="error-msg">
|
||||
@if (firstName.hasError('pattern') && firstName.touched) {
|
||||
<p>{{ "contactDialogForm.invalidPattern" | translate }}</p>
|
||||
|
|
@ -49,7 +49,7 @@
|
|||
required
|
||||
/>
|
||||
</div>
|
||||
@if (sharedService.isContactViewMedia) {
|
||||
@if (resizeService.isContactViewMedia) {
|
||||
<div class="error-msg">
|
||||
@if (lastName.hasError('pattern') && lastName.touched) {
|
||||
<p>{{ "contactDialogForm.invalidPattern" | translate }}</p>
|
||||
|
|
@ -57,7 +57,7 @@
|
|||
<p>{{ "contactDialogForm.invalidLastName" | translate }}</p>
|
||||
}}
|
||||
</div>
|
||||
} @if (!sharedService.isContactViewMedia) {
|
||||
} @if (!resizeService.isContactViewMedia) {
|
||||
<div class="error-msg">
|
||||
@if (firstName.hasError('pattern') || lastName.hasError('pattern') &&
|
||||
firstName.touched) {
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import { FirebaseService } from '../../../../services/firebase.service';
|
|||
import { FormBtnComponent } from '../../../../shared/components/buttons/form-btn/form-btn.component';
|
||||
import { User } from '../../../../interfaces/user.interface';
|
||||
import { Router } from '@angular/router';
|
||||
import { ResizeService } from '../../../../services/resize.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-contact-form',
|
||||
|
|
@ -32,6 +33,7 @@ export class ContactFormComponent implements OnInit, OnChanges {
|
|||
constructor(
|
||||
private router: Router,
|
||||
public firebaseService: FirebaseService,
|
||||
public resizeService: ResizeService,
|
||||
public sharedService: SharedService
|
||||
) {
|
||||
this.updateContactData();
|
||||
|
|
|
|||
34
src/app/services/color.service.ts
Normal file
34
src/app/services/color.service.ts
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
import { Injectable } from '@angular/core';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class ColorService {
|
||||
generateRandomColor(): string {
|
||||
const letters = '0123456789ABCDEF';
|
||||
let color = '#';
|
||||
let isGrayScale = true;
|
||||
|
||||
while (isGrayScale) {
|
||||
for (let i = 0; i < 6; i++) {
|
||||
color += letters[Math.floor(Math.random() * 16)];
|
||||
}
|
||||
const rgb = this.hexToRgb(color);
|
||||
if (rgb.r !== rgb.g || rgb.g !== rgb.b) {
|
||||
isGrayScale = false;
|
||||
} else {
|
||||
color = '#';
|
||||
}
|
||||
}
|
||||
|
||||
return color;
|
||||
}
|
||||
|
||||
hexToRgb(hex: string): { r: number; g: number; b: number } {
|
||||
const bigint = parseInt(hex.substring(1), 16);
|
||||
const r = (bigint >> 16) & 255;
|
||||
const g = (bigint >> 8) & 255;
|
||||
const b = bigint & 255;
|
||||
return { r, g, b };
|
||||
}
|
||||
}
|
||||
|
|
@ -25,6 +25,7 @@ import { Router } from '@angular/router';
|
|||
import CryptoES from 'crypto-es';
|
||||
import { CryptoESSecretKey } from '../environments/config';
|
||||
import { catchError, map, Observable, of } from 'rxjs';
|
||||
import { ColorService } from './color.service';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
|
|
@ -39,6 +40,7 @@ export class LoginService {
|
|||
|
||||
constructor(
|
||||
private firebaseService: FirebaseService,
|
||||
private colorService: ColorService,
|
||||
public sharedService: SharedService,
|
||||
private router: Router
|
||||
) {}
|
||||
|
|
@ -124,7 +126,7 @@ export class LoginService {
|
|||
? registerData.firstName.slice(0, 1).toUpperCase() +
|
||||
registerData.lastName.slice(0, 1).toUpperCase()
|
||||
: '',
|
||||
color: this.sharedService.generateRandomColor(),
|
||||
color: this.colorService.generateRandomColor(),
|
||||
lastLogin: new Date().getTime(),
|
||||
};
|
||||
this.createUserInFirestore(userDataToSave);
|
||||
|
|
@ -169,7 +171,7 @@ export class LoginService {
|
|||
isOnline: true,
|
||||
phone: '',
|
||||
initials: firstName.slice(0, 1) + lastName.slice(0, 1),
|
||||
color: this.sharedService.generateRandomColor(),
|
||||
color: this.colorService.generateRandomColor(),
|
||||
lastLogin: 0,
|
||||
});
|
||||
} else {
|
||||
|
|
|
|||
29
src/app/services/resize.service.ts
Normal file
29
src/app/services/resize.service.ts
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
import { Injectable } from '@angular/core';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class ResizeService {
|
||||
isPageViewMedia: boolean = window.innerWidth <= 650;
|
||||
isContactViewMedia: boolean = window.innerWidth <= 800;
|
||||
|
||||
private resizeListener!: () => void;
|
||||
|
||||
constructor() {
|
||||
this.initResizeListener();
|
||||
}
|
||||
|
||||
private initResizeListener() {
|
||||
this.resizeListener = this.onResize.bind(this);
|
||||
window.addEventListener('resize', this.resizeListener);
|
||||
}
|
||||
|
||||
private onResize() {
|
||||
this.isPageViewMedia = window.innerWidth <= 650;
|
||||
this.isContactViewMedia = window.innerWidth <= 800;
|
||||
}
|
||||
|
||||
cleanup() {
|
||||
window.removeEventListener('resize', this.resizeListener);
|
||||
}
|
||||
}
|
||||
|
|
@ -10,121 +10,5 @@ export class SharedService {
|
|||
isNewContactDialogOpen: boolean = false;
|
||||
isEditContactDialogOpen: boolean = false;
|
||||
isDeleteContactDialogOpen: boolean = false;
|
||||
isPageViewMedia: boolean = window.innerWidth <= 650;
|
||||
isContactViewMedia: boolean = window.innerWidth <= 800;
|
||||
currentUserId: string = '';
|
||||
|
||||
private resizeListener!: () => void;
|
||||
|
||||
constructor() {
|
||||
this.initResizeListener();
|
||||
}
|
||||
|
||||
// CHECK VIEW WIDTH
|
||||
|
||||
/**
|
||||
* Initializes the window resize listener.
|
||||
*
|
||||
* @remarks
|
||||
* This function will set up the window resize listener and bind the
|
||||
* onResize() method to it.
|
||||
*/
|
||||
private initResizeListener() {
|
||||
this.resizeListener = this.onResize.bind(this);
|
||||
window.addEventListener('resize', this.resizeListener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the window resize listener.
|
||||
*
|
||||
* @remarks
|
||||
* This function will remove the window resize listener that was set up in
|
||||
* the constructor. This is useful when the service is being destroyed.
|
||||
*/
|
||||
private removeResizeListener() {
|
||||
window.removeEventListener('resize', this.resizeListener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Window resize event listener method.
|
||||
*
|
||||
* @remarks
|
||||
* This method will update the isPageViewMedia and isContactViewMedia
|
||||
* properties based on the current window width whenever the window is
|
||||
* resized.
|
||||
*/
|
||||
private onResize() {
|
||||
this.isPageViewMedia = window.innerWidth <= 650;
|
||||
this.isContactViewMedia = window.innerWidth <= 800;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans up the service by removing the window resize listener.
|
||||
*
|
||||
* @remarks
|
||||
* This function is useful when the service is being destroyed.
|
||||
*/
|
||||
cleanup() {
|
||||
this.removeResizeListener();
|
||||
}
|
||||
|
||||
// RANDOM COLOR
|
||||
|
||||
/**
|
||||
* Generates a random color string.
|
||||
*
|
||||
* @remarks
|
||||
* The generated color is a hex string that is not a shade of gray.
|
||||
* The color is generated by randomly selecting a hex digit from the
|
||||
* string '0123456789ABCDEF' and appending it to the color string. The
|
||||
* loop continues until a non-gray color is generated.
|
||||
*
|
||||
* @returns A random color string.
|
||||
*/
|
||||
generateRandomColor(): string {
|
||||
const letters = '0123456789ABCDEF';
|
||||
let color = '#';
|
||||
let isGrayScale = true;
|
||||
|
||||
while (isGrayScale) {
|
||||
for (let i = 0; i < 6; i++) {
|
||||
color += letters[Math.floor(Math.random() * 16)];
|
||||
}
|
||||
const rgb = this.hexToRgb(color);
|
||||
if (rgb.r !== rgb.g || rgb.g !== rgb.b) {
|
||||
isGrayScale = false;
|
||||
} else {
|
||||
color = '#';
|
||||
}
|
||||
}
|
||||
|
||||
return color;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a hex color string to an RGB object.
|
||||
*
|
||||
* @param hex A color string in hex format, e.g. '#FF0000'.
|
||||
* @returns An object with r, g, and b properties, each an integer between
|
||||
* 0 and 255, representing the red, green, and blue components of the color.
|
||||
*/
|
||||
hexToRgb(hex: string): { r: number; g: number; b: number } {
|
||||
const bigint = parseInt(hex.substring(1), 16);
|
||||
const r = (bigint >> 16) & 255;
|
||||
const g = (bigint >> 8) & 255;
|
||||
const b = bigint & 255;
|
||||
return { r, g, b };
|
||||
}
|
||||
|
||||
// SECURITY
|
||||
|
||||
/**
|
||||
* Sanitizes the input string by replacing any potential cross-site scripting characters.
|
||||
* This ensures that the input string is safe to be stored in the Firebase Realtime Database.
|
||||
* @param input The input string to be sanitized.
|
||||
* @returns The sanitized string.
|
||||
*/
|
||||
replaceXSSChars(input: string) {
|
||||
return input.replace(/</g, '<').replace(/>/g, '>');
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue