Merge pull request #63 from HideyoshiNakazone/better-typing-and-optimizations

Better Typing and Optimizations
This commit is contained in:
2023-12-29 07:03:51 -03:00
committed by GitHub
21 changed files with 105 additions and 145 deletions

View File

@@ -18,7 +18,10 @@
"main": "src/main.ts", "main": "src/main.ts",
"polyfills": "src/polyfills.ts", "polyfills": "src/polyfills.ts",
"tsConfig": "tsconfig.app.json", "tsConfig": "tsconfig.app.json",
"allowedCommonJsDependencies": ["ts-interface-checker"], "allowedCommonJsDependencies": [
"ts-interface-checker",
"apexcharts"
],
"assets": [ "assets": [
"src/assets", "src/assets",
"src/manifest.webmanifest", "src/manifest.webmanifest",

14
package-lock.json generated
View File

@@ -25,6 +25,7 @@
"@fortawesome/free-regular-svg-icons": "^6.1.1", "@fortawesome/free-regular-svg-icons": "^6.1.1",
"@fortawesome/free-solid-svg-icons": "^6.1.1", "@fortawesome/free-solid-svg-icons": "^6.1.1",
"@glidejs/glide": "^3.6.0", "@glidejs/glide": "^3.6.0",
"@sinclair/typebox": "^0.32.4",
"apexcharts": "^3.45.1", "apexcharts": "^3.45.1",
"bootstrap": "^4.6.2", "bootstrap": "^4.6.2",
"cookieconsent": "^3.1.1", "cookieconsent": "^3.1.1",
@@ -3291,6 +3292,12 @@
"node": "^14.15.0 || ^16.10.0 || >=18.0.0" "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
} }
}, },
"node_modules/@jest/schemas/node_modules/@sinclair/typebox": {
"version": "0.27.8",
"resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz",
"integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==",
"dev": true
},
"node_modules/@jridgewell/gen-mapping": { "node_modules/@jridgewell/gen-mapping": {
"version": "0.3.3", "version": "0.3.3",
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz",
@@ -4670,10 +4677,9 @@
} }
}, },
"node_modules/@sinclair/typebox": { "node_modules/@sinclair/typebox": {
"version": "0.27.8", "version": "0.32.4",
"resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.32.4.tgz",
"integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", "integrity": "sha512-Q2Pex6H+HGuyxIGuFadkpqwtjZFXiVZlvy1rVX9XgAzUrDmUDEM69M2c4CkWUgMJ1NaFPvUf+cMBljY96GJVNQ=="
"dev": true
}, },
"node_modules/@socket.io/component-emitter": { "node_modules/@socket.io/component-emitter": {
"version": "3.1.0", "version": "3.1.0",

View File

@@ -33,6 +33,7 @@
"@fortawesome/free-regular-svg-icons": "^6.1.1", "@fortawesome/free-regular-svg-icons": "^6.1.1",
"@fortawesome/free-solid-svg-icons": "^6.1.1", "@fortawesome/free-solid-svg-icons": "^6.1.1",
"@glidejs/glide": "^3.6.0", "@glidejs/glide": "^3.6.0",
"@sinclair/typebox": "^0.32.4",
"apexcharts": "^3.45.1", "apexcharts": "^3.45.1",
"bootstrap": "^4.6.2", "bootstrap": "^4.6.2",
"cookieconsent": "^3.1.1", "cookieconsent": "^3.1.1",

View File

@@ -7,7 +7,6 @@ import {
} from '@angular/animations'; } from '@angular/animations';
import { import {
Component, Component,
ComponentRef,
EventEmitter, EventEmitter,
Input, Input,
OnDestroy, OnDestroy,
@@ -16,7 +15,6 @@ import {
ViewContainerRef, ViewContainerRef,
} from '@angular/core'; } from '@angular/core';
import { import {
faEdit,
faQuestionCircle, faSignIn, faQuestionCircle, faSignIn,
faSignOutAlt, faSignOutAlt,
faUser, faUser,
@@ -24,10 +22,8 @@ import {
import { Subscription } from 'rxjs'; import { Subscription } from 'rxjs';
import { AuthService } from 'src/app/shared/service/auth.service'; import { AuthService } from 'src/app/shared/service/auth.service';
import { User } from '../../shared/model/user/user.model'; import { User } from '../../shared/model/user/user.model';
import UserChecker from '../../shared/model/user/user.checker';
import { HelpComponent } from '../header-popup/help/help.component';
import { MyProfileComponent } from '../header-popup/my-profile/my-profile.component';
import {IconDefinition} from "@fortawesome/free-regular-svg-icons"; import {IconDefinition} from "@fortawesome/free-regular-svg-icons";
import {Value} from "@sinclair/typebox/value";
@Component({ @Component({
selector: 'app-header-dropdown', selector: 'app-header-dropdown',
@@ -124,7 +120,7 @@ export class HeaderDropdownComponent implements OnInit, OnDestroy {
ngOnInit(): void { ngOnInit(): void {
this.userSubscription = this.authService.authSubject.subscribe( this.userSubscription = this.authService.authSubject.subscribe(
(res) => { (res) => {
if (res && UserChecker.test(res)) { if (res && Value.Check(User, res)) {
this.user = <User>res; this.user = <User>res;
} else { } else {
this.user = null; this.user = null;

View File

@@ -14,9 +14,7 @@ import { DomSanitizer } from '@angular/platform-browser';
import { faLock, faUser } from '@fortawesome/free-solid-svg-icons'; import { faLock, faUser } from '@fortawesome/free-solid-svg-icons';
import { Subscription } from 'rxjs'; import { Subscription } from 'rxjs';
import { AuthService } from 'src/app/shared/service/auth.service'; import { AuthService } from 'src/app/shared/service/auth.service';
import { HttpError } from 'src/app/shared/model/httpError/httpError.model'; import { Value } from '@sinclair/typebox/value'
import HttpErrorChecker from 'src/app/shared/model/httpError/httpErrorChecker';
import UserChecker from 'src/app/shared/model/user/user.checker';
import { User } from 'src/app/shared/model/user/user.model'; import { User } from 'src/app/shared/model/user/user.model';
import { import {
animate, animate,
@@ -35,6 +33,7 @@ import {
NgcStatusChangeEvent, NgcStatusChangeEvent,
} from 'ngx-cookieconsent'; } from 'ngx-cookieconsent';
import { CookieConsertService } from '../../../shared/cookie-consent/cookie-consert.service'; import { CookieConsertService } from '../../../shared/cookie-consent/cookie-consert.service';
import {HttpError} from "../../../shared/model/httpError/httpError.model";
const GOOGLE_LOGO_SVG = 'assets/img/providers/google.svg'; const GOOGLE_LOGO_SVG = 'assets/img/providers/google.svg';
const GOOGLE_DISABLED_LOGO_SVG = 'assets/img/providers/google-disabled.svg'; const GOOGLE_DISABLED_LOGO_SVG = 'assets/img/providers/google-disabled.svg';
@@ -206,10 +205,10 @@ export class LoginComponent implements OnInit, AfterViewInit, OnDestroy {
} }
private validateLogin(res: User | HttpError | null) { private validateLogin(res: User | HttpError | null) {
if (res && UserChecker.test(res)) { if (res && Value.Check(User, res)) {
this.closePopup(); this.closePopup();
} }
if (HttpErrorChecker.test(res)) { if (Value.Check(HttpError, res)) {
this.errorMessage = (<HttpError>res).details; this.errorMessage = (<HttpError>res).details;
} }
} }

View File

@@ -1,5 +1,4 @@
import { import {
ChangeDetectorRef,
Component, Component,
EventEmitter, EventEmitter,
Input, Input,
@@ -18,15 +17,9 @@ import {
transition, transition,
trigger, trigger,
} from '@angular/animations'; } from '@angular/animations';
import { MatIconRegistry } from '@angular/material/icon';
import { DomSanitizer } from '@angular/platform-browser';
import { FormControl, FormGroup, Validators } from '@angular/forms'; import { FormControl, FormGroup, Validators } from '@angular/forms';
import { ValidateNotEmptyValidator } from '../../../shared/validators/validate-not-empty.validator'; import { ValidateNotEmptyValidator } from '../../../shared/validators/validate-not-empty.validator';
import { ValidatePasswordValidator } from '../../../shared/validators/validate-password.validator'; import { ValidatePasswordValidator } from '../../../shared/validators/validate-password.validator';
import { first, take } from 'rxjs';
import UserChecker from '../../../shared/model/user/user.checker';
import HttpErrorChecker from '../../../shared/model/httpError/httpErrorChecker';
import { HttpError } from '../../../shared/model/httpError/httpError.model';
import { faFileUpload } from '@fortawesome/free-solid-svg-icons'; import { faFileUpload } from '@fortawesome/free-solid-svg-icons';
@Component({ @Component({

View File

@@ -11,8 +11,6 @@ import {
import { Subscription } from 'rxjs'; import { Subscription } from 'rxjs';
import { AuthService } from 'src/app/shared/service/auth.service'; import { AuthService } from 'src/app/shared/service/auth.service';
import { HttpError } from 'src/app/shared/model/httpError/httpError.model'; import { HttpError } from 'src/app/shared/model/httpError/httpError.model';
import HttpErrorChecker from 'src/app/shared/model/httpError/httpErrorChecker';
import UserChecker from 'src/app/shared/model/user/user.checker';
import { User } from 'src/app/shared/model/user/user.model'; import { User } from 'src/app/shared/model/user/user.model';
import { import {
animate, animate,
@@ -27,6 +25,7 @@ import {
import { ValidateEmailValidator } from '../../../shared/validators/validate-email.validator'; import { ValidateEmailValidator } from '../../../shared/validators/validate-email.validator';
import { ValidatePasswordValidator } from '../../../shared/validators/validate-password.validator'; import { ValidatePasswordValidator } from '../../../shared/validators/validate-password.validator';
import { ValidateNotEmptyValidator } from '../../../shared/validators/validate-not-empty.validator'; import { ValidateNotEmptyValidator } from '../../../shared/validators/validate-not-empty.validator';
import {Value} from "@sinclair/typebox/value";
const GOOGLE_LOGO_SVG = 'assets/img/providers/google.svg'; const GOOGLE_LOGO_SVG = 'assets/img/providers/google.svg';
const GITHUB_LOGO_SVG = 'assets/img/providers/github.svg'; const GITHUB_LOGO_SVG = 'assets/img/providers/github.svg';
@@ -179,10 +178,10 @@ export class SignupComponent implements OnInit {
} }
private validateSignup(res: User | HttpError | null) { private validateSignup(res: User | HttpError | null) {
if (res && UserChecker.test(res)) { if (res && Value.Check(User, res)) {
this.closePopup(); this.closePopup();
} }
if (HttpErrorChecker.test(res)) { if (Value.Check(HttpError, res)) {
this.errorMessage = (<HttpError>res).details; this.errorMessage = (<HttpError>res).details;
} }
} }

View File

@@ -8,10 +8,10 @@ import {
} from '@angular/core'; } from '@angular/core';
import { faUser } from '@fortawesome/free-solid-svg-icons'; import { faUser } from '@fortawesome/free-solid-svg-icons';
import { SliderItemComponent } from 'src/app/shared/components/slider-item/slider-item.component'; import { SliderItemComponent } from 'src/app/shared/components/slider-item/slider-item.component';
import UserChecker from '../../../shared/model/user/user.checker';
import { User } from '../../../shared/model/user/user.model'; import { User } from '../../../shared/model/user/user.model';
import { AuthService } from '../../../shared/service/auth.service'; import { AuthService } from '../../../shared/service/auth.service';
import { Subscription } from 'rxjs'; import { Subscription } from 'rxjs';
import {Value} from "@sinclair/typebox/value";
@Component({ @Component({
selector: 'app-nav-slider', selector: 'app-nav-slider',
@@ -41,7 +41,7 @@ export class NavSliderComponent
ngOnInit(): void { ngOnInit(): void {
this.userSubscription = this.authService.authSubject.subscribe( this.userSubscription = this.authService.authSubject.subscribe(
(res) => { (res) => {
if (res && UserChecker.test(res)) { if (res && Value.Check(User, res)) {
this.loggedUser = <User>res; this.loggedUser = <User>res;
} else { } else {
this.loggedUser = null; this.loggedUser = null;

View File

@@ -2,8 +2,8 @@ import { Component, EventEmitter, OnInit, Output } from '@angular/core';
import { Subscription } from 'rxjs'; import { Subscription } from 'rxjs';
import { AuthService } from 'src/app/shared/service/auth.service'; import { AuthService } from 'src/app/shared/service/auth.service';
import { SliderItemComponent } from 'src/app/shared/components/slider-item/slider-item.component'; import { SliderItemComponent } from 'src/app/shared/components/slider-item/slider-item.component';
import UserChecker from 'src/app/shared/model/user/user.checker';
import { User } from 'src/app/shared/model/user/user.model'; import { User } from 'src/app/shared/model/user/user.model';
import {Value} from "@sinclair/typebox/value";
@Component({ @Component({
selector: 'app-user-slider', selector: 'app-user-slider',
@@ -70,7 +70,7 @@ export class UserSliderComponent extends SliderItemComponent implements OnInit {
ngOnInit() { ngOnInit() {
this.authSubscription = this.authService.authSubject.subscribe( this.authSubscription = this.authService.authSubject.subscribe(
(res) => { (res) => {
if (UserChecker.test(res)) { if (Value.Check(User, res)) {
this.user = <User>res; this.user = <User>res;
} else { } else {
this.user = null; this.user = null;

View File

@@ -5,17 +5,17 @@ import {
OnDestroy, OnDestroy,
OnInit, OnInit,
ViewChild, ViewChild,
ViewContainerRef, ViewEncapsulation, ViewContainerRef,
} from '@angular/core'; } from '@angular/core';
import { faUser } from '@fortawesome/free-solid-svg-icons'; import { faUser } from '@fortawesome/free-solid-svg-icons';
import { LoginComponent } from './header-popup/login/login.component'; import { LoginComponent } from './header-popup/login/login.component';
import { SignupComponent } from './header-popup/signup/signup.component'; import { SignupComponent } from './header-popup/signup/signup.component';
import { AuthService } from '../shared/service/auth.service'; import { AuthService } from '../shared/service/auth.service';
import UserChecker from '../shared/model/user/user.checker';
import { User } from '../shared/model/user/user.model'; import { User } from '../shared/model/user/user.model';
import { Subscription } from 'rxjs'; import { Subscription } from 'rxjs';
import { HelpComponent } from './header-popup/help/help.component'; import { HelpComponent } from './header-popup/help/help.component';
import { MyProfileComponent } from './header-popup/my-profile/my-profile.component'; import { MyProfileComponent } from './header-popup/my-profile/my-profile.component';
import {Value} from "@sinclair/typebox/value";
@Component({ @Component({
selector: 'app-header', selector: 'app-header',
@@ -66,7 +66,7 @@ export class HeaderComponent implements OnInit, OnDestroy {
ngOnInit(): void { ngOnInit(): void {
this.userSubscription = this.authService.authSubject.subscribe( this.userSubscription = this.authService.authSubject.subscribe(
(res) => { (res) => {
if (res && UserChecker.test(res)) { if (res && Value.Check(User, res)) {
this.loggedUser = <User>res; this.loggedUser = <User>res;
} else { } else {
this.loggedUser = null; this.loggedUser = null;

View File

@@ -48,8 +48,8 @@
margin: 5px 0px 5px 0px; margin: 5px 0px 5px 0px;
display: flex; display: flex;
flex-direction: row; flex-direction: row;
align-items: start; align-items: flex-start;
align-content: start; align-content: flex-start;
color: #919294; color: #919294;
} }

View File

@@ -1,18 +0,0 @@
/**
* This module was automatically generated by `ts-interface-builder`
*/
import * as t from 'ts-interface-checker';
// tslint:disable:object-literal-key-quotes
export const HttpError = t.iface([], {
title: 'string',
status: 'number',
details: 'string',
developerMessage: 'string',
timestamp: 'string',
});
const exportedTypeSuite: t.ITypeSuite = {
HttpError,
};
export default exportedTypeSuite;

View File

@@ -1,7 +1,12 @@
export interface HttpError { import { Type, type Static } from '@sinclair/typebox'
title: string;
status: number;
details: string; export const HttpError = Type.Object({
developerMessage: string; title: Type.String(),
timestamp: string; status: Type.Number(),
} details: Type.String(),
developerMessage: Type.String(),
timestamp: Type.String()
});
export type HttpError = Static<typeof HttpError>;

View File

@@ -1,5 +0,0 @@
import { createCheckers } from 'ts-interface-checker';
import HttpError from './httpError.model-ti';
const HttpErrorChecker = createCheckers(HttpError)['HttpError'];
export default HttpErrorChecker;

View File

@@ -1,18 +1,25 @@
export type Language = { import { Type, type Static } from '@sinclair/typebox'
name: string;
color: string;
percentage: number;
}
export type Project = {
name: string;
description: string;
link: string;
license?: string; export const Language = Type.Object({
languages?: Language[]; name: Type.String(),
color: Type.String(),
percentage: Type.Number(),
})
stars: number; export const Project = Type.Object({
forks: number; name: Type.String(),
watchers: number; description: Type.String(),
} link: Type.String(),
license: Type.Optional(Type.String()),
languages: Type.Optional(Type.Array(Language)),
stars: Type.Number(),
forks: Type.Number(),
watchers: Type.Number(),
});
export type Language = Static<typeof Language>;
export type Project = Static<typeof Project>;

View File

@@ -1,5 +1,11 @@
export interface Stack { import { Type, type Static } from '@sinclair/typebox'
name: string;
image: string;
description: string; export const Stack = Type.Object({
} name: Type.String(),
image: Type.String(),
description: Type.String()
});
export type Stack = Static<typeof Stack>;

View File

@@ -1,15 +0,0 @@
/**
* This module was automatically generated by `ts-interface-builder`
*/
import * as t from 'ts-interface-checker';
// tslint:disable:object-literal-key-quotes
export const Token = t.iface([], {
token: 'string',
expirationDate: t.union('string', 'number'),
});
const exportedTypeSuite: t.ITypeSuite = {
Token,
};
export default exportedTypeSuite;

View File

@@ -1,4 +1,9 @@
export interface Token { import { Type, type Static } from '@sinclair/typebox'
token: string;
expirationDate: string | number;
} export const Token = Type.Object({
token: Type.String(),
expirationDate: Type.Union([Type.String(), Type.Number()])
});
export type Token = Static<typeof Token>;

View File

@@ -1,6 +0,0 @@
import { createCheckers } from 'ts-interface-checker';
import User from './user.model-ti';
import Token from '../token/token.model-ti';
const UserChecker = createCheckers(User, Token)['User'];
export default UserChecker;

View File

@@ -1,22 +0,0 @@
/**
* This module was automatically generated by `ts-interface-builder`
*/
import * as t from 'ts-interface-checker';
// tslint:disable:object-literal-key-quotes
export const User = t.iface([], {
id: t.opt('number'),
name: t.opt('string'),
email: t.opt('string'),
username: 'string',
password: t.opt('string'),
profilePictureUrl: t.opt('string'),
accessToken: t.opt('Token'),
refreshToken: t.opt('Token'),
roles: t.opt(t.array('string')),
});
const exportedTypeSuite: t.ITypeSuite = {
User,
};
export default exportedTypeSuite;

View File

@@ -1,14 +1,20 @@
import { Type, type Static } from '@sinclair/typebox'
import { Token } from '../token/token.model'; import { Token } from '../token/token.model';
export interface User {
id?: number; export const User = Type.Object({
name?: string; id: Type.Optional(Type.Number()),
email?: string; name: Type.Optional(Type.String()),
username: string; email: Type.Optional(Type.String()),
password?: string; username: Type.String(),
profilePictureUrl?: string; password: Type.Optional(Type.String()),
accessToken?: Token; profilePictureUrl: Type.Optional(Type.String()),
refreshToken?: Token; accessToken: Type.Optional(Token),
roles?: Array<string>; refreshToken: Type.Optional(Token),
validateAccessToken?: () => Token | undefined; roles: Type.Optional(Type.Array(Type.String())),
} validateAccessToken: Type.Optional(Type.Function([],Type.Optional(Token)))
});
export type User = Static<typeof User>;