feat: update app version and build details; refactor auth service and components for improved functionality

This commit is contained in:
2026-05-16 16:11:58 +03:30
parent 3f75d82295
commit fe09aa4931
10 changed files with 124 additions and 40 deletions
+34 -15
View File
@@ -6,8 +6,8 @@ import { HttpClient } from '@angular/common/http';
import { computed, inject, Injectable, signal } from '@angular/core';
import { Router } from '@angular/router';
import { CookieService } from 'ngx-cookie-service';
import { BehaviorSubject, Observable, throwError } from 'rxjs';
import { catchError, finalize, tap } from 'rxjs/operators';
import { BehaviorSubject, from, Observable, throwError } from 'rxjs';
import { catchError, finalize, switchMap, tap } from 'rxjs/operators';
import config from 'src/config';
import { COOKIE_KEYS, LOCAL_STORAGE_KEYS } from '../../../assets/constants';
import {
@@ -82,11 +82,14 @@ export class AuthService {
},
)
.pipe(
tap((response) => {
this.handleAuthSuccess(response);
this.refreshCookies();
return response;
}),
switchMap((response) =>
from(
this.handleAuthSuccess(response).then(() => {
this.refreshCookies();
return response;
}),
),
),
catchError((error) => {
console.error('Login error:', error);
this.isLoadingSubject.next(false);
@@ -98,19 +101,19 @@ export class AuthService {
);
}
logout(): void {
async logout(): Promise<void> {
localStorage.removeItem(LOCAL_STORAGE_KEYS.AUTH_TOKEN);
localStorage.removeItem(LOCAL_STORAGE_KEYS.USER_DATA);
localStorage.removeItem(LOCAL_STORAGE_KEYS.REFRESH_TOKEN);
this.refreshCookies();
await this.refreshCookies();
this.setCurrentUser(null);
await this.setCurrentUser(null);
this.router.navigate(['/auth']);
}
refreshCookies() {
this.cookieService.set(COOKIE_KEYS.POS_ID, '', new Date());
this.cookieService.set(COOKIE_KEYS.ACCESS_TOKEN, '', new Date());
async refreshCookies() {
await this.cookieService.set(COOKIE_KEYS.POS_ID, '', new Date());
await this.cookieService.set(COOKIE_KEYS.ACCESS_TOKEN, '', new Date());
}
doRefreshToken(): Observable<IAuthResponse> {
@@ -171,6 +174,22 @@ export class AuthService {
return localStorage.getItem(LOCAL_STORAGE_KEYS.AUTH_TOKEN);
}
waitForAuthReady(maxRetries = 20): Promise<boolean> {
return new Promise((resolve) => {
let retries = 0;
const check = () => {
const isReady = Boolean(this.getToken()) && Boolean(this.currentAccount());
if (isReady || retries >= maxRetries) {
resolve(isReady);
return;
}
retries += 1;
setTimeout(check, 0);
};
check();
});
}
/**
* Get current refresh token
*/
@@ -247,7 +266,7 @@ export class AuthService {
return true;
}
private handleAuthSuccess(response: IAuthResponse): void {
private async handleAuthSuccess(response: IAuthResponse): Promise<void> {
localStorage.setItem(LOCAL_STORAGE_KEYS.AUTH_TOKEN, response.accessToken);
this.token.set(response.accessToken);
this.refreshToken.set(response.refreshToken);
@@ -260,7 +279,7 @@ export class AuthService {
// this.navigateByRole(response.user.role);
}
private setCurrentUser(account: Maybe<IAuthAccountResponse>): void {
private async setCurrentUser(account: Maybe<IAuthAccountResponse>): Promise<void> {
this.currentAccount.set(account);
this.currentAccountSubject.next(account);
localStorage.setItem(LOCAL_STORAGE_KEYS.USER_DATA, JSON.stringify(account));
@@ -25,13 +25,13 @@
<!-- [ngStyle]="{ transform: 'scale(1.' + pullDistance() * 10 + ')' }" -->
</div>
}
@if (isAuth()) {
<!-- @if (isAuth()) {
@defer (when isAuth()) {
<router-outlet></router-outlet>
}
} @else {
<pos-pages-layout />
}
} @else { -->
<pos-pages-layout />
<!-- } -->
</div>
}
}
@@ -0,0 +1 @@
export * from './routes/index';
@@ -0,0 +1,19 @@
import { NamedRoutes } from '@/core';
import { Routes } from '@angular/router';
export type TPosAuthRouteNames = 'login';
const baseRoute = `auth`;
export const posAuthNamedRoutes: NamedRoutes<TPosAuthRouteNames> = {
login: {
path: 'auth',
loadComponent: () => import('../../views/auth.component').then((m) => m.PosAuthComponent),
meta: {
title: 'ورود',
pagePath: () => baseRoute,
},
},
};
export const POS_AUTH_ROUTES: Routes = [posAuthNamedRoutes.login];
@@ -0,0 +1 @@
<app-auth (onSubmit)="onLoggedIn($event)" />
@@ -0,0 +1,18 @@
import { AuthComponent } from '@/modules/auth/pages/auth.component';
import { Component, inject } from '@angular/core';
import { Router } from '@angular/router';
@Component({
selector: 'pos-auth',
templateUrl: 'auth.component.html',
imports: [AuthComponent],
})
export class PosAuthComponent {
private readonly router = inject(Router);
onLoggedIn(isReady: boolean) {
if (isReady) {
this.router.navigateByUrl('/');
}
}
}
+13 -8
View File
@@ -1,4 +1,5 @@
import { IAuthResponse, TRoles } from '@/core';
import { AuthService } from '@/core/services/auth.service';
import { ToastService } from '@/core/services/toast.service';
import { Component, EventEmitter, inject, Input, Output, signal } from '@angular/core';
import { Router } from '@angular/router';
@@ -19,10 +20,11 @@ export class AuthComponent {
@Input() role?: TRoles;
@Input() defaultStep: TSteps = 'login';
@Output() submit = new EventEmitter<Promise<boolean>>();
@Output() onSubmit = new EventEmitter<boolean>();
private readonly toastService = inject(ToastService);
private readonly router = inject(Router);
private readonly authService = inject(AuthService);
readonly logo = images.logo;
readonly authVector = images.login;
@@ -36,7 +38,7 @@ export class AuthComponent {
this.activeStep.set('signup');
};
onLoggedIn = (data: IAuthResponse) => {
onLoggedIn = async (data: IAuthResponse) => {
this.toastService.success({ text: 'شما با موفقیت وارد شدید.' });
this.selectedRole.set(data.account.type.toUpperCase() as TRoles);
@@ -46,15 +48,18 @@ export class AuthComponent {
// return;
// }
if (this.submit.observed) {
return this.submit.emit(Promise.resolve(true));
if (this.onSubmit.observed) {
const isReady = await this.authService.waitForAuthReady();
return this.onSubmit.emit(isReady);
} else {
this.redirectToDashboard();
}
this.redirectToDashboard();
};
onModifyLoginInfo = () => {
if (this.submit.observed) {
return this.submit.emit(Promise.resolve(true));
onModifyLoginInfo = async () => {
if (this.onSubmit.observed) {
const isReady = await this.authService.waitForAuthReady();
return this.onSubmit.emit(isReady);
}
this.redirectToDashboard();
};
@@ -101,9 +101,11 @@ export class LoginComponent {
next: (data) => {
this.errorMessage.set('');
if (this.redirectUrl) {
this.router.navigateByUrl(this.redirectUrl.replace(/\/[^/]*$/, ''));
this.navigateAfterAuth(this.redirectUrl.replace(/\/[^/]*$/, ''));
} else {
this.onSuccessfullySubmit.emit(data);
this.waitForAuthState().then(() => {
this.onSuccessfullySubmit.emit(data);
});
}
},
error: (error) => {
@@ -113,4 +115,25 @@ export class LoginComponent {
},
});
}
private navigateAfterAuth(url: string) {
this.waitForAuthState().then(() => {
this.router.navigateByUrl(url);
});
}
private waitForAuthState(maxRetries = 10): Promise<void> {
return new Promise((resolve) => {
let retries = 0;
const check = () => {
if (this.authService.getToken() || retries >= maxRetries) {
resolve();
return;
}
retries += 1;
setTimeout(check, 0);
};
check();
});
}
}
+6 -8
View File
@@ -1,3 +1,4 @@
import { posAuthNamedRoutes } from '@/domains/pos/modules/auth/constants';
import { POS_ROUTES } from '@/domains/pos/routes';
import { Notfound } from '@/pages/notfound/notfound.component';
import { Routes } from '@angular/router';
@@ -7,14 +8,11 @@ export const appRoutes: Routes = [
path: '',
loadComponent: () =>
import('@/domains/pos/layouts/layout.component').then((m) => m.PosLayoutComponent),
children: [
POS_ROUTES,
{
path: 'auth',
loadComponent: () =>
import('@/modules/auth/pages/auth.component').then((m) => m.AuthComponent),
},
],
children: [POS_ROUTES],
},
{
...posAuthNamedRoutes.login,
path: 'auth',
},
{ path: 'notfound', component: Notfound },
{ path: '**', redirectTo: '/notfound' },