From d2fd65cd0eb292419cb4edab5176d7aebeaa1924 Mon Sep 17 00:00:00 2001 From: "Mustafa Alsalfiti (XignSys)" <49871916+mustafasalfiti@users.noreply.github.com> Date: Mon, 3 Jun 2024 17:55:27 +0200 Subject: [PATCH] Feat: Add dark design (#54) * feat: add dark design choice Signed-off-by: Mirko Mollik * Feat/extension (#47) * fix: set correct docker image names Signed-off-by: Mirko Mollik * fix: extension Signed-off-by: Mirko Mollik --------- Signed-off-by: Mirko Mollik * fix: extension Signed-off-by: Mirko Mollik * fix: readme Signed-off-by: Mustafa Alsalfiti * fix: holder-app test Signed-off-by: Mustafa Alsalfiti * remove comment from theme file Signed-off-by: Mirko Mollik * fix: remove warnings from ci build Signed-off-by: Mirko Mollik * fix: verifier build Signed-off-by: Mirko Mollik --------- Signed-off-by: Mirko Mollik Signed-off-by: Mirko Mollik Signed-off-by: Mustafa Alsalfiti Co-authored-by: Mirko Mollik Co-authored-by: Mirko Mollik --- README.md | 4 +- apps/holder-app/src/app/app.component.scss | 12 ++-- apps/holder-app/src/app/app.component.spec.ts | 5 +- apps/holder-app/src/app/app.component.ts | 13 +++- apps/holder-app/src/app/app.config.ts | 2 +- apps/holder-app/src/app/app.routes.ts | 2 +- apps/holder-app/src/app/auth/auth.service.ts | 7 +- .../src/app/scanner/scanner.component.ts | 2 +- apps/holder-app/src/index.html | 4 +- apps/holder-app/src/styles.scss | 1 - apps/holder-app/src/theme.scss | 32 +++++++-- .../app/settings/entities/setting.entity.ts | 7 +- apps/holder-browser-extension/project.json | 9 ++- .../src/app/app.component.scss | 2 - .../src/app/app.component.ts | 1 + .../src/app/app.config.ts | 33 ++++----- .../src/app/app.routes.ts | 2 +- .../src/app/auth/auth.guard.ts | 11 ++- .../src/app/auth/auth.service.ts | 28 ++++++-- .../src/app/scanner/scanner.component.ts | 2 +- .../src/app/scanner/scanner.service.ts | 5 +- .../environments/environment.development.ts | 2 +- .../src/environments/environment.ts | 6 +- apps/holder-browser-extension/src/index.html | 4 +- apps/holder-browser-extension/src/theme.scss | 33 +++++++-- apps/verifier-frontend/project.json | 7 +- docker/keycloak.Dockerfile | 5 +- .../realm-export.json => docker/realm.json | 0 libs/holder-shared/src/index.ts | 1 + .../src/lib/api/api/credentials.service.ts | 4 +- .../src/lib/api/api/default.service.ts | 4 +- .../src/lib/api/api/history.service.ts | 4 +- .../src/lib/api/api/oid4vci.service.ts | 4 +- .../src/lib/api/api/oid4vcp.service.ts | 4 +- .../src/lib/api/api/settings.service.ts | 4 +- .../src/lib/api/model/setting.ts | 1 + .../src/lib/api/model/settingResponse.ts | 1 + .../src/lib/api/model/updateSettingsDto.ts | 1 + .../src/lib/settings/settings.component.html | 17 +++-- .../src/lib/settings/settings.component.ts | 38 +++++----- .../src/lib/settings/settings.service.ts | 16 +++++ .../src/lib/key/filesystem-key.service.ts | 1 - package.json | 1 - pnpm-lock.yaml | 72 ------------------- tsconfig.base.json | 2 +- 45 files changed, 227 insertions(+), 189 deletions(-) rename config/keycloak/realm-export.json => docker/realm.json (100%) diff --git a/README.md b/README.md index b5d2ac0c..306324ad 100644 --- a/README.md +++ b/README.md @@ -3,9 +3,9 @@ CredHub is comprehensive monorepo including a cloud wallet for natural persons together with a minimal issuer and verifier service. The cloud wallet will host all credentials and key pairs, including the business logic to receive and present credentials. # Why a cloud wallet -A cloud wallet is able to move the whole complexity of the SSI algorithms to the server side, so the clients only need to render the data. This makes the development of new clients or integration into existing applications much easier. It also provides an equal security level for all users amd does not exclude any smartphones because of their hardware capabilities. Besides it allows the user to access his credentials from multiple devices without the need to sync them. +A cloud wallet is able to move the whole complexity of the SSI algorithms to the server side, so the clients only need to render the data. This makes the development of new clients or integration into existing applications much easier. It also provides an equal security level for all users and does not exclude any smartphones because of their hardware capabilities. Besides that it allows the user to access his credentials from multiple devices without the need to sync them. -Of course the user is loosing offline capabilities and has to trust the server to not misuse personal data. But this is a tradeoff that can be acceptable for many use cases when you want to start with verifiable credentials with great user experience and low development effort. +Of course the user is losing offline capabilities and has to trust the server to not misuse personal data. But this is a tradeoff that can be acceptable for many use cases when you want to start with verifiable credentials with great user experience and low development effort. # Tech Stack - Programming language: Typescript, Node >= v18 diff --git a/apps/holder-app/src/app/app.component.scss b/apps/holder-app/src/app/app.component.scss index ed16f27a..245fa8a4 100644 --- a/apps/holder-app/src/app/app.component.scss +++ b/apps/holder-app/src/app/app.component.scss @@ -1,18 +1,16 @@ -.mat-toolbar { - background-color: #D7E3FF; +.mat-toolbar{ padding: 10px 40px; height: 84px; position: fixed; bottom: 0px; } -.info { - font-size: 14px; -} - .content { height: calc(100vh - 84px); overflow: auto; width: 100%; - padding-bottom: 84px; +} + +.info { + font-size: 14px; } diff --git a/apps/holder-app/src/app/app.component.spec.ts b/apps/holder-app/src/app/app.component.spec.ts index b5915a15..80241eee 100644 --- a/apps/holder-app/src/app/app.component.spec.ts +++ b/apps/holder-app/src/app/app.component.spec.ts @@ -4,11 +4,14 @@ import { AppComponent } from './app.component'; import { CheckForUpdatesService } from './check-for-updates.service'; import { SwUpdate } from '@angular/service-worker'; import { of } from 'rxjs'; +import { SettingsService } from '@my-wallet/holder-shared'; +import { HttpClientModule } from '@angular/common/http'; describe('AppComponent', () => { let component: AppComponent; let fixture: ComponentFixture; let checkForUpdatesService: CheckForUpdatesService; + let settingsService: SettingsService; beforeAll(() => { Object.defineProperty(window, 'matchMedia', { @@ -24,7 +27,7 @@ describe('AppComponent', () => { beforeEach(() => { TestBed.configureTestingModule({ - imports: [RouterTestingModule, AppComponent], + imports: [RouterTestingModule, AppComponent, HttpClientModule], providers: [ CheckForUpdatesService, { provide: SwUpdate, useValue: { available: of() } }, // Mock SwUpdate diff --git a/apps/holder-app/src/app/app.component.ts b/apps/holder-app/src/app/app.component.ts index a7849e06..2d563834 100644 --- a/apps/holder-app/src/app/app.component.ts +++ b/apps/holder-app/src/app/app.component.ts @@ -1,4 +1,4 @@ -import { Component } from '@angular/core'; +import { Component, OnInit } from '@angular/core'; import { CommonModule } from '@angular/common'; import { RouterLink, RouterLinkActive, RouterOutlet } from '@angular/router'; import { MatToolbarModule } from '@angular/material/toolbar'; @@ -7,6 +7,7 @@ import { FlexLayoutModule } from 'ng-flex-layout'; import { MatMenuModule } from '@angular/material/menu'; import { MatIconModule } from '@angular/material/icon'; import { CheckForUpdatesService } from './check-for-updates.service'; +import { SettingsService } from '@my-wallet/holder-shared'; @Component({ selector: 'app-root', @@ -26,6 +27,12 @@ import { CheckForUpdatesService } from './check-for-updates.service'; templateUrl: './app.component.html', styleUrl: './app.component.scss', }) -export class AppComponent { - constructor(private checkForUpdatesService: CheckForUpdatesService) {} +export class AppComponent implements OnInit { + constructor( + private checkForUpdatesService: CheckForUpdatesService, + private settingsService: SettingsService + ) {} + async ngOnInit(): Promise { + this.settingsService.setThemeToApplication(); + } } diff --git a/apps/holder-app/src/app/app.config.ts b/apps/holder-app/src/app/app.config.ts index 363038eb..c52723a9 100644 --- a/apps/holder-app/src/app/app.config.ts +++ b/apps/holder-app/src/app/app.config.ts @@ -16,7 +16,7 @@ import { ApiModule, Configuration, AuthServiceInterface, -} from '@my-wallet/-holder-shared'; +} from '@my-wallet/holder-shared'; import { AuthService } from './auth/auth.service'; import { provideServiceWorker } from '@angular/service-worker'; diff --git a/apps/holder-app/src/app/app.routes.ts b/apps/holder-app/src/app/app.routes.ts index d82cf762..9f9adf44 100644 --- a/apps/holder-app/src/app/app.routes.ts +++ b/apps/holder-app/src/app/app.routes.ts @@ -8,7 +8,7 @@ import { HistoryListComponent, HistoryShowComponent, SettingsComponent, -} from '@my-wallet/-holder-shared'; +} from '@my-wallet/holder-shared'; export const routes: Routes = [ { diff --git a/apps/holder-app/src/app/auth/auth.service.ts b/apps/holder-app/src/app/auth/auth.service.ts index 0cf9199b..e11a6cde 100644 --- a/apps/holder-app/src/app/auth/auth.service.ts +++ b/apps/holder-app/src/app/auth/auth.service.ts @@ -6,7 +6,8 @@ import { OAuthService } from 'angular-oauth2-oidc'; import { BehaviorSubject, combineLatest, Observable } from 'rxjs'; import { filter, map } from 'rxjs/operators'; import { authConfig } from '../authConfig'; -import { AuthServiceInterface } from '@my-wallet/-holder-shared'; +import { AuthServiceInterface } from '@my-wallet/holder-shared'; +import { environment } from '../../environments/environment'; @Injectable({ providedIn: 'root' }) export class AuthService implements AuthServiceInterface { @@ -40,6 +41,10 @@ export class AuthService implements AuthServiceInterface { this.init(); } + getSettingsLink(): string { + return `${environment.keycloakHost}/realms/${environment.keycloakRealm}/account`; + } + private init() { // Useful for debugging: // this.oauthService.events.subscribe((event) => { diff --git a/apps/holder-app/src/app/scanner/scanner.component.ts b/apps/holder-app/src/app/scanner/scanner.component.ts index 6380db18..0d446ef0 100644 --- a/apps/holder-app/src/app/scanner/scanner.component.ts +++ b/apps/holder-app/src/app/scanner/scanner.component.ts @@ -8,7 +8,7 @@ import { CommonModule } from '@angular/common'; import { VerifyRequestComponent, IssuanceRequestComponent, -} from '@my-wallet/-holder-shared'; +} from '@my-wallet/holder-shared'; import { MatDividerModule } from '@angular/material/divider'; import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; import { FlexLayoutModule } from 'ng-flex-layout'; diff --git a/apps/holder-app/src/index.html b/apps/holder-app/src/index.html index f4a7a753..a46e549e 100644 --- a/apps/holder-app/src/index.html +++ b/apps/holder-app/src/index.html @@ -1,4 +1,4 @@ - + @@ -16,7 +16,7 @@ - + globalThis.token, - }, - }); -} - export const appConfig: ApplicationConfig = { providers: [ provideRouter(routes), { provide: LocationStrategy, useClass: HashLocationStrategy }, provideAnimations(), + provideHttpClient(), provideOAuthClient(), importProvidersFrom(ApiModule), { provide: MAT_DIALOG_DEFAULT_OPTIONS, useValue: { hasBackdrop: true } }, @@ -43,8 +28,18 @@ export const appConfig: ApplicationConfig = { useClass: AuthService, }, { + //TODO: maybe instead of a factory, we can use a class where we inject a provider to fetch the token. provide: Configuration, - useFactory: getConfiguration, + useFactory: (authService: AuthService) => + new Configuration({ + //TODO: the basepath is static, therefore we can not set it during the login process. + basePath: environment.backendUrl, + credentials: { + oauth2: authService.getToken.bind(authService), + }, + }), + deps: [AuthService], + multi: false, }, ], }; diff --git a/apps/holder-browser-extension/src/app/app.routes.ts b/apps/holder-browser-extension/src/app/app.routes.ts index ac3e0dad..3c2a3671 100644 --- a/apps/holder-browser-extension/src/app/app.routes.ts +++ b/apps/holder-browser-extension/src/app/app.routes.ts @@ -8,7 +8,7 @@ import { HistoryListComponent, HistoryShowComponent, SettingsComponent, -} from '@my-wallet/-holder-shared'; +} from '@my-wallet/holder-shared'; export const routes: Routes = [ { path: '', diff --git a/apps/holder-browser-extension/src/app/auth/auth.guard.ts b/apps/holder-browser-extension/src/app/auth/auth.guard.ts index 0dc06d9b..46d1cc83 100644 --- a/apps/holder-browser-extension/src/app/auth/auth.guard.ts +++ b/apps/holder-browser-extension/src/app/auth/auth.guard.ts @@ -1,18 +1,17 @@ import { inject } from '@angular/core'; import { CanActivateFn } from '@angular/router'; import { AuthService } from './auth.service'; - -// eslint-disable-next-line @typescript-eslint/no-namespace -export declare namespace globalThis { - let token: string; -} +import { SettingsService } from '@my-wallet/holder-shared'; export const authGuard: CanActivateFn = async () => { const authService: AuthService = inject(AuthService); + const settingsService = inject(SettingsService); return authService .isAuthenticated() .then(async () => { - globalThis.token = await authService.getToken(); + await authService.setToken(); + //set the theme here. We can not do this in the app.component because it gets loaded before the login process is finished. + settingsService.setThemeToApplication(); return true; }) .catch(() => { diff --git a/apps/holder-browser-extension/src/app/auth/auth.service.ts b/apps/holder-browser-extension/src/app/auth/auth.service.ts index 7f3befe3..9236b451 100644 --- a/apps/holder-browser-extension/src/app/auth/auth.service.ts +++ b/apps/holder-browser-extension/src/app/auth/auth.service.ts @@ -1,8 +1,8 @@ import { Injectable } from '@angular/core'; import { environment } from '../../environments/environment'; import { decodeJwt } from 'jose'; -import { Router } from '@angular/router'; import { BehaviorSubject } from 'rxjs'; +import { AuthServiceInterface } from '@my-wallet/holder-shared'; interface Storage { access_token: string; @@ -13,10 +13,22 @@ interface Storage { @Injectable({ providedIn: 'root', }) -export class AuthService { +export class AuthService implements AuthServiceInterface { changed: BehaviorSubject = new BehaviorSubject(false); - constructor(private router: Router) {} + private token?: string; + + getToken() { + return this.token as string; + } + + /** + * Returns the settings link to manage authentication. + * @returns + */ + getSettingsLink(): string { + return `${environment.keycloakHost}/realms/${environment.keycloakRealm}/account`; + } /** * Checks if the access token exists and if it is expired @@ -40,11 +52,12 @@ export class AuthService { * Gets the access token from the storage * @returns */ - getToken(): Promise { + setToken(): Promise { return new Promise((resolve) => { chrome.storage.local.get('access_token', (values) => { const token = (values as Storage).access_token; - resolve(token); + this.token = token; + resolve(); }); }); } @@ -54,6 +67,7 @@ export class AuthService { */ async login() { if (typeof chrome.identity !== 'undefined') { + console.log(this.getAuthUrl()); await chrome.identity .launchWebAuthFlow({ interactive: true, @@ -169,7 +183,9 @@ export class AuthService { `${environment.keycloakHost}/realms/${environment.keycloakRealm}/protocol/openid-connect/logout` + `?client_id=${environment.keycloakClient}` + `&id_token_hint=${idToken}` + - `&post_logout_redirect_uri=${encodeURIComponent(chrome.identity.getRedirectURL(''))}`; + `&post_logout_redirect_uri=${encodeURIComponent( + chrome.identity.getRedirectURL('') + )}`; resolve(logoutUrl); }); }); diff --git a/apps/holder-browser-extension/src/app/scanner/scanner.component.ts b/apps/holder-browser-extension/src/app/scanner/scanner.component.ts index d9fad3fa..a6ca7bb7 100644 --- a/apps/holder-browser-extension/src/app/scanner/scanner.component.ts +++ b/apps/holder-browser-extension/src/app/scanner/scanner.component.ts @@ -11,7 +11,7 @@ import { HttpClient } from '@angular/common/http'; import { IssuanceRequestComponent, VerifyRequestComponent, -} from '@my-wallet/-holder-shared'; +} from '@my-wallet/holder-shared'; import { ActivatedRoute } from '@angular/router'; @Component({ diff --git a/apps/holder-browser-extension/src/app/scanner/scanner.service.ts b/apps/holder-browser-extension/src/app/scanner/scanner.service.ts index c895a92e..fcd4d5ca 100644 --- a/apps/holder-browser-extension/src/app/scanner/scanner.service.ts +++ b/apps/holder-browser-extension/src/app/scanner/scanner.service.ts @@ -5,10 +5,7 @@ import { CredentialsSupportedDisplay, MetadataDisplay, } from '@sphereon/oid4vci-common'; -import { - Oid4vciApiService, - Oid4vcpApiService, -} from '@my-wallet/-holder-shared'; +import { Oid4vciApiService, Oid4vcpApiService } from '@my-wallet/holder-shared'; import { firstValueFrom } from 'rxjs'; export interface ResultScan { diff --git a/apps/holder-browser-extension/src/environments/environment.development.ts b/apps/holder-browser-extension/src/environments/environment.development.ts index 39057070..a5081dc5 100644 --- a/apps/holder-browser-extension/src/environments/environment.development.ts +++ b/apps/holder-browser-extension/src/environments/environment.development.ts @@ -11,7 +11,7 @@ export declare namespace globalThis { export const environment = { backendUrl: 'http://localhost:3000', keycloakHost: 'http://localhost:8080', - keycloakClient: 'browser', + keycloakClient: 'wallet', keycloakRealm: 'wallet', }; diff --git a/apps/holder-browser-extension/src/environments/environment.ts b/apps/holder-browser-extension/src/environments/environment.ts index 39057070..558145bd 100644 --- a/apps/holder-browser-extension/src/environments/environment.ts +++ b/apps/holder-browser-extension/src/environments/environment.ts @@ -9,9 +9,9 @@ export declare namespace globalThis { } export const environment = { - backendUrl: 'http://localhost:3000', - keycloakHost: 'http://localhost:8080', - keycloakClient: 'browser', + backendUrl: 'https://backend.credhub.eu', + keycloakHost: 'http://auth.credhub.eu', + keycloakClient: 'wallet', keycloakRealm: 'wallet', }; diff --git a/apps/holder-browser-extension/src/index.html b/apps/holder-browser-extension/src/index.html index 998d0aa1..baf4e0fb 100644 --- a/apps/holder-browser-extension/src/index.html +++ b/apps/holder-browser-extension/src/index.html @@ -1,4 +1,4 @@ - + @@ -10,7 +10,7 @@ rel="stylesheet" /> - + diff --git a/apps/holder-browser-extension/src/theme.scss b/apps/holder-browser-extension/src/theme.scss index 48c58a3a..6b6977e2 100644 --- a/apps/holder-browser-extension/src/theme.scss +++ b/apps/holder-browser-extension/src/theme.scss @@ -1,12 +1,11 @@ @use '@angular/material' as mat; -@use '@angular/material-experimental' as matx; @import '@angular/cdk/overlay-prebuilt.css'; +@include mat.core(); -$theme: mat.define-theme(( +$light-theme: mat.define-theme(( color: ( theme-type: light, primary: mat.$azure-palette, - tertiary: mat.$blue-palette, ), typography: ( brand-family: '"Roboto", sans-serif', @@ -17,9 +16,29 @@ $theme: mat.define-theme(( ) )); -@include mat.core(); -@include mat.color-variants-backwards-compatibility($theme); +$dark-theme: mat.define-theme(( + color: ( + theme-type: dark, + primary: mat.$orange-palette, + ), +)); + +@mixin background($theme) { + .mat-toolbar { + @if mat.get-theme-type($theme) == dark { + background: mat.get-theme-color($theme, primary, 10); + } @else { + background: mat.get-theme-color($theme, primary, 90); + } + } +} -:root { - @include mat.all-component-themes($theme); +html { + // Apply the dark theme by default + @include mat.all-component-themes($light-theme); + @include background($light-theme); + .dark-theme { + @include mat.all-component-colors($dark-theme); + @include background($dark-theme); + } } diff --git a/apps/verifier-frontend/project.json b/apps/verifier-frontend/project.json index 88c9f265..a3d28916 100644 --- a/apps/verifier-frontend/project.json +++ b/apps/verifier-frontend/project.json @@ -32,15 +32,16 @@ "@angular/material/prebuilt-themes/indigo-pink.css", "apps/verifier-frontend/src/styles.scss" ], - "scripts": [] + "scripts": [], + "allowedCommonJsDependencies": ["qrcode"] }, "configurations": { "production": { "budgets": [ { "type": "initial", - "maximumWarning": "500kb", - "maximumError": "1mb" + "maximumWarning": "1mb", + "maximumError": "2mb" }, { "type": "anyComponentStyle", diff --git a/docker/keycloak.Dockerfile b/docker/keycloak.Dockerfile index 4f6d5d1f..9bafb4d4 100644 --- a/docker/keycloak.Dockerfile +++ b/docker/keycloak.Dockerfile @@ -30,6 +30,9 @@ USER root RUN sed -i '/disabledAlgorithms/ s/ SHA1,//' /etc/crypto-policies/back-ends/java.config USER keycloak +# copy the default realm config +COPY ./docker/realm.json /opt/keycloak/data/import/realm.json + RUN /opt/keycloak/bin/kc.sh show-config -ENTRYPOINT ["/opt/keycloak/bin/kc.sh"] \ No newline at end of file +ENTRYPOINT ["/opt/keycloak/bin/kc.sh"] diff --git a/config/keycloak/realm-export.json b/docker/realm.json similarity index 100% rename from config/keycloak/realm-export.json rename to docker/realm.json diff --git a/libs/holder-shared/src/index.ts b/libs/holder-shared/src/index.ts index d5a8fe7d..34a0ad9a 100644 --- a/libs/holder-shared/src/index.ts +++ b/libs/holder-shared/src/index.ts @@ -6,3 +6,4 @@ export * from './lib/credentials/credentials-list/credentials-list.component'; export * from './lib/credentials/credentials-show/credentials-show.component'; export * from './lib/history/history-list/history-list.component'; export * from './lib/history/history-show/history-show.component'; +export * from './lib/settings/settings.service'; diff --git a/libs/holder-shared/src/lib/api/api/credentials.service.ts b/libs/holder-shared/src/lib/api/api/credentials.service.ts index 3d635a08..d5315638 100644 --- a/libs/holder-shared/src/lib/api/api/credentials.service.ts +++ b/libs/holder-shared/src/lib/api/api/credentials.service.ts @@ -12,7 +12,9 @@ /* tslint:disable:no-unused-variable member-ordering */ import { Inject, Injectable, Optional } from '@angular/core'; -import { HttpClient, HttpHeaders, HttpParams, HttpResponse, HttpEvent, HttpParameterCodec, HttpContext } from '@angular/common/http'; +import { HttpClient, HttpHeaders, HttpParams, + HttpResponse, HttpEvent, HttpParameterCodec, HttpContext + } from '@angular/common/http'; import { CustomHttpParameterCodec } from '../encoder'; import { Observable } from 'rxjs'; diff --git a/libs/holder-shared/src/lib/api/api/default.service.ts b/libs/holder-shared/src/lib/api/api/default.service.ts index a005c03b..15c9a19c 100644 --- a/libs/holder-shared/src/lib/api/api/default.service.ts +++ b/libs/holder-shared/src/lib/api/api/default.service.ts @@ -12,7 +12,9 @@ /* tslint:disable:no-unused-variable member-ordering */ import { Inject, Injectable, Optional } from '@angular/core'; -import { HttpClient, HttpHeaders, HttpParams, HttpResponse, HttpEvent, HttpParameterCodec, HttpContext } from '@angular/common/http'; +import { HttpClient, HttpHeaders, HttpParams, + HttpResponse, HttpEvent, HttpParameterCodec, HttpContext + } from '@angular/common/http'; import { CustomHttpParameterCodec } from '../encoder'; import { Observable } from 'rxjs'; diff --git a/libs/holder-shared/src/lib/api/api/history.service.ts b/libs/holder-shared/src/lib/api/api/history.service.ts index ca1c4880..ce65f5f8 100644 --- a/libs/holder-shared/src/lib/api/api/history.service.ts +++ b/libs/holder-shared/src/lib/api/api/history.service.ts @@ -12,7 +12,9 @@ /* tslint:disable:no-unused-variable member-ordering */ import { Inject, Injectable, Optional } from '@angular/core'; -import { HttpClient, HttpHeaders, HttpParams, HttpResponse, HttpEvent, HttpParameterCodec, HttpContext } from '@angular/common/http'; +import { HttpClient, HttpHeaders, HttpParams, + HttpResponse, HttpEvent, HttpParameterCodec, HttpContext + } from '@angular/common/http'; import { CustomHttpParameterCodec } from '../encoder'; import { Observable } from 'rxjs'; diff --git a/libs/holder-shared/src/lib/api/api/oid4vci.service.ts b/libs/holder-shared/src/lib/api/api/oid4vci.service.ts index b40a2388..8bdcf280 100644 --- a/libs/holder-shared/src/lib/api/api/oid4vci.service.ts +++ b/libs/holder-shared/src/lib/api/api/oid4vci.service.ts @@ -12,7 +12,9 @@ /* tslint:disable:no-unused-variable member-ordering */ import { Inject, Injectable, Optional } from '@angular/core'; -import { HttpClient, HttpHeaders, HttpParams, HttpResponse, HttpEvent, HttpParameterCodec, HttpContext } from '@angular/common/http'; +import { HttpClient, HttpHeaders, HttpParams, + HttpResponse, HttpEvent, HttpParameterCodec, HttpContext + } from '@angular/common/http'; import { CustomHttpParameterCodec } from '../encoder'; import { Observable } from 'rxjs'; diff --git a/libs/holder-shared/src/lib/api/api/oid4vcp.service.ts b/libs/holder-shared/src/lib/api/api/oid4vcp.service.ts index ca7b4a09..46fe4da3 100644 --- a/libs/holder-shared/src/lib/api/api/oid4vcp.service.ts +++ b/libs/holder-shared/src/lib/api/api/oid4vcp.service.ts @@ -12,7 +12,9 @@ /* tslint:disable:no-unused-variable member-ordering */ import { Inject, Injectable, Optional } from '@angular/core'; -import { HttpClient, HttpHeaders, HttpParams, HttpResponse, HttpEvent, HttpParameterCodec, HttpContext } from '@angular/common/http'; +import { HttpClient, HttpHeaders, HttpParams, + HttpResponse, HttpEvent, HttpParameterCodec, HttpContext + } from '@angular/common/http'; import { CustomHttpParameterCodec } from '../encoder'; import { Observable } from 'rxjs'; diff --git a/libs/holder-shared/src/lib/api/api/settings.service.ts b/libs/holder-shared/src/lib/api/api/settings.service.ts index 3856995a..072aeda6 100644 --- a/libs/holder-shared/src/lib/api/api/settings.service.ts +++ b/libs/holder-shared/src/lib/api/api/settings.service.ts @@ -12,7 +12,9 @@ /* tslint:disable:no-unused-variable member-ordering */ import { Inject, Injectable, Optional } from '@angular/core'; -import { HttpClient, HttpHeaders, HttpParams, HttpResponse, HttpEvent, HttpParameterCodec, HttpContext } from '@angular/common/http'; +import { HttpClient, HttpHeaders, HttpParams, + HttpResponse, HttpEvent, HttpParameterCodec, HttpContext + } from '@angular/common/http'; import { CustomHttpParameterCodec } from '../encoder'; import { Observable } from 'rxjs'; diff --git a/libs/holder-shared/src/lib/api/model/setting.ts b/libs/holder-shared/src/lib/api/model/setting.ts index a763468e..dc0e3e46 100644 --- a/libs/holder-shared/src/lib/api/model/setting.ts +++ b/libs/holder-shared/src/lib/api/model/setting.ts @@ -14,5 +14,6 @@ export interface Setting { user: string; auto: boolean; + darkTheme: boolean; } diff --git a/libs/holder-shared/src/lib/api/model/settingResponse.ts b/libs/holder-shared/src/lib/api/model/settingResponse.ts index 5dd63f01..199fca4a 100644 --- a/libs/holder-shared/src/lib/api/model/settingResponse.ts +++ b/libs/holder-shared/src/lib/api/model/settingResponse.ts @@ -13,5 +13,6 @@ export interface SettingResponse { auto: boolean; + darkTheme: boolean; } diff --git a/libs/holder-shared/src/lib/api/model/updateSettingsDto.ts b/libs/holder-shared/src/lib/api/model/updateSettingsDto.ts index 25629870..75a26009 100644 --- a/libs/holder-shared/src/lib/api/model/updateSettingsDto.ts +++ b/libs/holder-shared/src/lib/api/model/updateSettingsDto.ts @@ -13,5 +13,6 @@ export interface UpdateSettingsDto { auto: boolean; + darkTheme: boolean; } diff --git a/libs/holder-shared/src/lib/settings/settings.component.html b/libs/holder-shared/src/lib/settings/settings.component.html index d95a08cd..fba1b11b 100644 --- a/libs/holder-shared/src/lib/settings/settings.component.html +++ b/libs/holder-shared/src/lib/settings/settings.component.html @@ -1,9 +1,16 @@ - - Automate issuance and verify process - +
+ + Automate issuance and verify process + + + Activate dark theme + +
Update password diff --git a/libs/holder-shared/src/lib/settings/settings.component.ts b/libs/holder-shared/src/lib/settings/settings.component.ts index d51a47e2..5c09f406 100644 --- a/libs/holder-shared/src/lib/settings/settings.component.ts +++ b/libs/holder-shared/src/lib/settings/settings.component.ts @@ -2,24 +2,15 @@ import { Component, OnInit } from '@angular/core'; import { MatButtonModule } from '@angular/material/button'; import { MatSlideToggleModule } from '@angular/material/slide-toggle'; import { SettingsService } from './settings.service'; -import { FormControl, ReactiveFormsModule } from '@angular/forms'; +import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms'; import { FlexLayoutModule } from 'ng-flex-layout'; import { MatListModule } from '@angular/material/list'; import { HttpClient } from '@angular/common/http'; import { firstValueFrom } from 'rxjs'; import { SettingsApiService } from '../api'; -// eslint-disable-next-line @typescript-eslint/no-namespace -export declare namespace globalThis { - let environment: { - backendUrl: string; - keycloakHost: string; - keycloakClient: string; - keycloakRealm: string; - }; -} - export abstract class AuthServiceInterface { + abstract getSettingsLink(): string; abstract logout(): void; } @@ -35,10 +26,9 @@ export abstract class AuthServiceInterface { FlexLayoutModule, MatListModule, ], - // providers: [provideHttpClient()], }) export class SettingsComponent implements OnInit { - automateControl!: FormControl; + form!: FormGroup; keycloakLink: string; constructor( @@ -47,28 +37,40 @@ export class SettingsComponent implements OnInit { private httpClient: HttpClient, private settingsApiService: SettingsApiService ) { - this.automateControl = new FormControl(); - this.keycloakLink = `${globalThis.environment.keycloakHost}/realms/${globalThis.environment.keycloakRealm}/account`; + this.form = new FormGroup({ + auto: new FormControl(false), + darkTheme: new FormControl(false), + }); + this.keycloakLink = this.authService.getSettingsLink(); } async ngOnInit(): Promise { const settings = await firstValueFrom( this.settingsApiService.settingsControllerGetSettings() ); - this.automateControl.setValue(settings.auto); - this.automateControl.valueChanges.subscribe(async (value) => { + this.form.setValue(settings); + this.form.valueChanges.subscribe(async (value) => { await firstValueFrom( this.settingsApiService.settingsControllerSetSettings({ - auto: value as boolean, + auto: value.auto, + darkTheme: value.darkTheme, }) ); }); + this.form.get('darkTheme')?.valueChanges.subscribe((value) => { + if (value) { + document.body.classList.add('dark-theme'); + } else { + document.body.classList.remove('dark-theme'); + } + }); } async showLicense() { const licencse = await firstValueFrom( this.httpClient.get('/3rdpartylicenses.txt', { responseType: 'text' }) ); + //TODO: print this in a dialog since alert is limited to the size alert(licencse); } } diff --git a/libs/holder-shared/src/lib/settings/settings.service.ts b/libs/holder-shared/src/lib/settings/settings.service.ts index bb4e9d64..ad61e50f 100644 --- a/libs/holder-shared/src/lib/settings/settings.service.ts +++ b/libs/holder-shared/src/lib/settings/settings.service.ts @@ -13,4 +13,20 @@ export class SettingsService { this.settingsService.settingsControllerGetSettings() ).then((res) => res.auto); } + + private getDarkTheme() { + return firstValueFrom( + this.settingsService.settingsControllerGetSettings() + ).then((res) => res.darkTheme); + } + + setThemeToApplication() { + this.getDarkTheme().then((darkTheme) => { + if (darkTheme) { + document.body.classList.add('dark-theme'); + } else { + document.body.classList.remove('dark-theme'); + } + }); + } } diff --git a/libs/relying-party-shared/src/lib/key/filesystem-key.service.ts b/libs/relying-party-shared/src/lib/key/filesystem-key.service.ts index 7132874c..41717293 100644 --- a/libs/relying-party-shared/src/lib/key/filesystem-key.service.ts +++ b/libs/relying-party-shared/src/lib/key/filesystem-key.service.ts @@ -13,7 +13,6 @@ import { KeyService } from './key.service'; import { Injectable } from '@nestjs/common'; import { Signer } from '@sd-jwt/types'; import { ConfigService } from '@nestjs/config'; -import { join } from 'node:path'; //TODO: implement a vault integration like in the backend /** diff --git a/package.json b/package.json index 7ee129f7..6ef91142 100644 --- a/package.json +++ b/package.json @@ -79,7 +79,6 @@ "@angular/core": "18.0.0", "@angular/forms": "18.0.0", "@angular/material": "18.0.0", - "@angular/material-experimental": "^18.0.0", "@angular/platform-browser": "18.0.0", "@angular/platform-browser-dynamic": "18.0.0", "@angular/router": "18.0.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 29216daa..62ee67c6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -34,9 +34,6 @@ dependencies: '@angular/material': specifier: 18.0.0 version: 18.0.0(@angular/animations@18.0.0)(@angular/cdk@18.0.0)(@angular/common@18.0.0)(@angular/core@18.0.0)(@angular/forms@18.0.0)(@angular/platform-browser@18.0.0)(rxjs@7.8.1) - '@angular/material-experimental': - specifier: ^18.0.0 - version: 18.0.0(@angular/animations@18.0.0)(@angular/cdk@18.0.0)(@angular/common@18.0.0)(@angular/core@18.0.0)(@angular/forms@18.0.0)(@angular/material@18.0.0)(@angular/platform-browser@18.0.0) '@angular/platform-browser': specifier: 18.0.0 version: 18.0.0(@angular/animations@18.0.0)(@angular/common@18.0.0)(@angular/core@18.0.0) @@ -885,75 +882,6 @@ packages: engines: {node: ^18.13.0 || >=20.9.0} dev: true - /@angular/material-experimental@18.0.0(@angular/animations@18.0.0)(@angular/cdk@18.0.0)(@angular/common@18.0.0)(@angular/core@18.0.0)(@angular/forms@18.0.0)(@angular/material@18.0.0)(@angular/platform-browser@18.0.0): - resolution: {integrity: sha512-5JFMKHXanX5bDBRNr7iLWG6DwjUzrWs3pf8Qg0pJIfGGFdU1UHtgFkSE/h1rob4opUbPI0pOGW3v/N81MnyDiw==} - peerDependencies: - '@angular/animations': ^18.0.0 || ^19.0.0 - '@angular/cdk': 18.0.0 - '@angular/common': ^18.0.0 || ^19.0.0 - '@angular/core': ^18.0.0 || ^19.0.0 - '@angular/forms': ^18.0.0 || ^19.0.0 - '@angular/material': 18.0.0 - '@angular/platform-browser': ^18.0.0 || ^19.0.0 - dependencies: - '@angular/animations': 18.0.0(@angular/core@18.0.0) - '@angular/cdk': 18.0.0(@angular/common@18.0.0)(@angular/core@18.0.0)(rxjs@7.8.1) - '@angular/common': 18.0.0(@angular/core@18.0.0)(rxjs@7.8.1) - '@angular/core': 18.0.0(rxjs@7.8.1)(zone.js@0.14.5) - '@angular/forms': 18.0.0(@angular/common@18.0.0)(@angular/core@18.0.0)(@angular/platform-browser@18.0.0)(rxjs@7.8.1) - '@angular/material': 18.0.0(@angular/animations@18.0.0)(@angular/cdk@18.0.0)(@angular/common@18.0.0)(@angular/core@18.0.0)(@angular/forms@18.0.0)(@angular/platform-browser@18.0.0)(rxjs@7.8.1) - '@angular/platform-browser': 18.0.0(@angular/animations@18.0.0)(@angular/common@18.0.0)(@angular/core@18.0.0) - '@material/animation': 15.0.0-canary.7f224ddd4.0 - '@material/auto-init': 15.0.0-canary.7f224ddd4.0 - '@material/banner': 15.0.0-canary.7f224ddd4.0 - '@material/base': 15.0.0-canary.7f224ddd4.0 - '@material/button': 15.0.0-canary.7f224ddd4.0 - '@material/card': 15.0.0-canary.7f224ddd4.0 - '@material/checkbox': 15.0.0-canary.7f224ddd4.0 - '@material/chips': 15.0.0-canary.7f224ddd4.0 - '@material/circular-progress': 15.0.0-canary.7f224ddd4.0 - '@material/data-table': 15.0.0-canary.7f224ddd4.0 - '@material/density': 15.0.0-canary.7f224ddd4.0 - '@material/dialog': 15.0.0-canary.7f224ddd4.0 - '@material/dom': 15.0.0-canary.7f224ddd4.0 - '@material/drawer': 15.0.0-canary.7f224ddd4.0 - '@material/elevation': 15.0.0-canary.7f224ddd4.0 - '@material/fab': 15.0.0-canary.7f224ddd4.0 - '@material/feature-targeting': 15.0.0-canary.7f224ddd4.0 - '@material/floating-label': 15.0.0-canary.7f224ddd4.0 - '@material/form-field': 15.0.0-canary.7f224ddd4.0 - '@material/icon-button': 15.0.0-canary.7f224ddd4.0 - '@material/image-list': 15.0.0-canary.7f224ddd4.0 - '@material/layout-grid': 15.0.0-canary.7f224ddd4.0 - '@material/line-ripple': 15.0.0-canary.7f224ddd4.0 - '@material/linear-progress': 15.0.0-canary.7f224ddd4.0 - '@material/list': 15.0.0-canary.7f224ddd4.0 - '@material/menu': 15.0.0-canary.7f224ddd4.0 - '@material/menu-surface': 15.0.0-canary.7f224ddd4.0 - '@material/notched-outline': 15.0.0-canary.7f224ddd4.0 - '@material/radio': 15.0.0-canary.7f224ddd4.0 - '@material/ripple': 15.0.0-canary.7f224ddd4.0 - '@material/rtl': 15.0.0-canary.7f224ddd4.0 - '@material/segmented-button': 15.0.0-canary.7f224ddd4.0 - '@material/select': 15.0.0-canary.7f224ddd4.0 - '@material/shape': 15.0.0-canary.7f224ddd4.0 - '@material/slider': 15.0.0-canary.7f224ddd4.0 - '@material/snackbar': 15.0.0-canary.7f224ddd4.0 - '@material/switch': 15.0.0-canary.7f224ddd4.0 - '@material/tab': 15.0.0-canary.7f224ddd4.0 - '@material/tab-bar': 15.0.0-canary.7f224ddd4.0 - '@material/tab-indicator': 15.0.0-canary.7f224ddd4.0 - '@material/tab-scroller': 15.0.0-canary.7f224ddd4.0 - '@material/textfield': 15.0.0-canary.7f224ddd4.0 - '@material/theme': 15.0.0-canary.7f224ddd4.0 - '@material/tokens': 15.0.0-canary.7f224ddd4.0 - '@material/tooltip': 15.0.0-canary.7f224ddd4.0 - '@material/top-app-bar': 15.0.0-canary.7f224ddd4.0 - '@material/touch-target': 15.0.0-canary.7f224ddd4.0 - '@material/typography': 15.0.0-canary.7f224ddd4.0 - tslib: 2.6.2 - dev: false - /@angular/material@18.0.0(@angular/animations@18.0.0)(@angular/cdk@18.0.0)(@angular/common@18.0.0)(@angular/core@18.0.0)(@angular/forms@18.0.0)(@angular/platform-browser@18.0.0)(rxjs@7.8.1): resolution: {integrity: sha512-4WfMcr4cX3cF7dKz+cXf9YIvhWOJGTP24rbMF5C6eC5K20IK6zgA//Qn0VSTwZkm54Tu9C7kF+CfNLeLy6i5uQ==} peerDependencies: diff --git a/tsconfig.base.json b/tsconfig.base.json index 2160035e..99cb7198 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -16,7 +16,7 @@ "forceConsistentCasingInFileNames": true, "baseUrl": ".", "paths": { - "@my-wallet/-holder-shared": ["libs/holder-shared/src/index.ts"], + "@my-wallet/holder-shared": ["libs/holder-shared/src/index.ts"], "@my-wallet/relying-party-shared": [ "libs/relying-party-shared/src/index.ts" ]