diff --git a/CHANGELOG.md b/CHANGELOG.md index be8ad0b..ec7e673 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/), and this - Support for subdomain gateways - Strict mode - More icons +- IPFS colours ### Changed diff --git a/src/app/app.component.html b/src/app/app.component.html index 0680b43..4bd6ac4 100644 --- a/src/app/app.component.html +++ b/src/app/app.component.html @@ -1 +1,10 @@ + +

Public IPFS Cacher

+ + + + +
diff --git a/src/app/app.component.ts b/src/app/app.component.ts index ce04c59..89751d8 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -1,4 +1,5 @@ import { Component } from '@angular/core'; +import { ThemeService } from './services/theme.service'; @Component({ selector: 'app-root', @@ -6,5 +7,7 @@ import { Component } from '@angular/core'; styleUrls: ['./app.component.scss'] }) export class AppComponent { - title = 'public-gateway-cacher'; + constructor( + public readonly themeService: ThemeService + ) { } } diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 6ff3c49..b18577a 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -1,6 +1,10 @@ import { CommonModule } from '@angular/common'; import { HttpClientModule } from '@angular/common/http'; import { NgModule } from '@angular/core'; +import { FlexLayoutModule } from '@angular/flex-layout'; +import { MatButtonModule } from '@angular/material/button'; +import { MatIconModule } from '@angular/material/icon'; +import { MatToolbarModule } from '@angular/material/toolbar'; import { BrowserModule } from '@angular/platform-browser'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { AppRoutingModule } from './app-routing.module'; @@ -16,6 +20,12 @@ import { AppComponent } from './app.component'; BrowserAnimationsModule, HttpClientModule, AppRoutingModule, + + // Material + FlexLayoutModule, + MatButtonModule, + MatIconModule, + MatToolbarModule, ], providers: [], bootstrap: [AppComponent] diff --git a/src/app/enums/theme.enum.ts b/src/app/enums/theme.enum.ts new file mode 100644 index 0000000..037550b --- /dev/null +++ b/src/app/enums/theme.enum.ts @@ -0,0 +1,4 @@ +export enum Theme { + Light = 'theme-light', + Dark = 'theme-dark', +} diff --git a/src/app/pages/pages.component.html b/src/app/pages/pages.component.html index 25b413d..1e54d31 100644 --- a/src/app/pages/pages.component.html +++ b/src/app/pages/pages.component.html @@ -1,12 +1,10 @@
-

Public IPFS Cacher

-
- + IPFS @@ -20,7 +18,7 @@ - + IPNS @@ -30,7 +28,8 @@ - + @@ -47,15 +46,15 @@
Gateway -
-
+
+ -
+
{{ element.gateway }} diff --git a/src/app/pages/pages.component.ts b/src/app/pages/pages.component.ts index 52cc2dd..5765ca0 100644 --- a/src/app/pages/pages.component.ts +++ b/src/app/pages/pages.component.ts @@ -1,21 +1,25 @@ import { HttpErrorResponse } from '@angular/common/http'; -import { Component, OnInit, ViewChild } from '@angular/core'; +import { Component, EventEmitter, OnDestroy, OnInit, ViewChild } from '@angular/core'; import { MatTable, MatTableDataSource } from '@angular/material/table'; import { Subscription } from 'rxjs'; +import { takeUntil } from 'rxjs/operators'; import { environment } from '../../environments/environment'; import { Protocol } from '../enums/protocol.enum'; +import { Theme } from '../enums/theme.enum'; import { GatewayService } from '../services/gateway.service'; +import { ThemeService } from '../services/theme.service'; @Component({ selector: 'app-pages', templateUrl: './pages.component.html', styleUrls: ['./pages.component.scss'] }) -export class PagesComponent implements OnInit { +export class PagesComponent implements OnInit, OnDestroy { @ViewChild(MatTable) matTable!: MatTable; gateways!: string[]; + inputColour = 'primary'; ipfs = ''; ipns = ''; @@ -23,12 +27,27 @@ export class PagesComponent implements OnInit { readonly displayedColumns: ['icon', 'gateway'] = ['icon', 'gateway']; readonly subscriptions: Subscription[] = []; + private readonly destroy$ = new EventEmitter(); + constructor( - private readonly gatewayService: GatewayService + private readonly gatewayService: GatewayService, + private readonly themeService: ThemeService ) { } ngOnInit(): void { this.gatewayService.list().subscribe((gateways): void => { this.gateways = gateways; }); + + // Theme + this.setColours(this.themeService.current); + this.themeService.valueChanges.pipe( + takeUntil(this.destroy$) + ).subscribe((theme): void => { + this.setColours(theme); + }); + } + + ngOnDestroy(): void { + this.destroy$.complete(); } cacheIPFS(): void { @@ -62,14 +81,16 @@ export class PagesComponent implements OnInit { this.dataSource.data.push({ gateway: this.gatewayService.url(gateway, protocol, hashpath), message: resp.statusText, - icon: this.icon(resp.status) + icon: this.getIcon(resp.status), + ok: resp.ok, }); this.matTable.renderRows(); }, (error: HttpErrorResponse): void => { this.dataSource.data.push({ gateway: this.gatewayService.url(gateway, protocol, hashpath), message: error.statusText, - icon: this.icon(error.status) + icon: this.getIcon(error.status), + ok: error.ok, }); this.matTable.renderRows(); }) @@ -77,7 +98,20 @@ export class PagesComponent implements OnInit { }); } - private icon(status: number): string { + private setColours(theme: Theme): void { + switch (theme) { + case Theme.Light: + this.inputColour = 'primary'; + break; + case Theme.Dark: + this.inputColour = 'accent'; + break; + default: + break; + } + } + + private getIcon(status: number): string { if (status >= 200 && status < 300) return '✅'; switch (status) { case 0: return '❌'; @@ -94,4 +128,5 @@ interface Result { gateway: string; message: string; icon: string; + ok: boolean; } diff --git a/src/app/services/gateway.service.ts b/src/app/services/gateway.service.ts index 28b7f8d..4e38e4f 100644 --- a/src/app/services/gateway.service.ts +++ b/src/app/services/gateway.service.ts @@ -1,4 +1,4 @@ -import { HttpClient, HttpResponseBase } from '@angular/common/http'; +import { HttpClient, HttpResponse } from '@angular/common/http'; import { Injectable } from '@angular/core'; import { Observable } from 'rxjs'; import { environment } from '../../environments/environment'; @@ -26,9 +26,10 @@ export class GatewayService { throw new Error('Couldn\'t find environment nor base.') } - get(gateway: string, protocol: Protocol, hashpath: string): Observable { - return this.http.get(`${this.url(gateway, protocol, hashpath)}#x-ipfs-companion-no-redirect`, { - observe: 'response' + get(gateway: string, protocol: Protocol, hashpath: string): Observable> { + return this.http.get(`${this.url(gateway, protocol, hashpath)}#x-ipfs-companion-no-redirect`, { + observe: 'response', + responseType: 'text', }); } diff --git a/src/app/services/theme.service.spec.ts b/src/app/services/theme.service.spec.ts new file mode 100644 index 0000000..092f96f --- /dev/null +++ b/src/app/services/theme.service.spec.ts @@ -0,0 +1,15 @@ +import { TestBed } from '@angular/core/testing'; +import { ThemeService } from './theme.service'; + +describe('ThemeService', (): void => { + let service: ThemeService; + + beforeEach((): void => { + TestBed.configureTestingModule({}); + service = TestBed.inject(ThemeService); + }); + + it('should be created', (): void => { + expect(service).toBeTruthy(); + }); +}); diff --git a/src/app/services/theme.service.ts b/src/app/services/theme.service.ts new file mode 100644 index 0000000..64242c2 --- /dev/null +++ b/src/app/services/theme.service.ts @@ -0,0 +1,49 @@ +import { Injectable } from '@angular/core'; +import { Observable, Subject } from 'rxjs'; +import { Theme } from '../enums/theme.enum'; + +function enumGuard(enumeration: T): (token: unknown) => token is T[keyof T] { + return (token: unknown): token is T[keyof T] => Object.values(enumeration).includes(token); +} + +@Injectable({ + providedIn: 'root' +}) +export class ThemeService { + + current = Theme.Light; + icon = 'brightness_low'; + readonly valueChanges: Observable; + + private readonly subject$ = new Subject(); + + constructor() { + const stored = localStorage.getItem('theme'); + if (enumGuard(Theme)(stored)) { + this.setTheme(stored); + } + + this.valueChanges = this.subject$.asObservable(); + } + + switchTheme(): void { + this.setTheme(this.current === Theme.Light ? Theme.Dark : Theme.Light) + } + + setTheme(theme: Theme): void { + document.querySelector('body')?.classList.remove(this.current) + document.querySelector('body')?.classList.add(theme) + this.current = theme; + this.icon = this.getIcon(theme) + this.subject$.next(theme); + localStorage.setItem('theme', theme); + } + + getIcon(theme: Theme): string { + switch (theme) { + case Theme.Dark: return 'brightness_high'; + case Theme.Light: + default: return 'brightness_low'; + } + } +} diff --git a/src/index.html b/src/index.html index e155162..d324920 100644 --- a/src/index.html +++ b/src/index.html @@ -19,7 +19,7 @@ - + diff --git a/src/styles.scss b/src/styles.scss index fd7f9d7..4bc4b87 100644 --- a/src/styles.scss +++ b/src/styles.scss @@ -8,22 +8,32 @@ // Be sure that you only ever include this mixin once! @include mat-core(); -// Define the palettes for your theme using the Material Design palettes available in palette.scss -// (imported above). For each palette, you can optionally specify a default, lighter, and darker -// hue. Available color palettes: https://material.io/design/color/ -$public-gateway-cacher-primary: mat-palette($mat-indigo); -$public-gateway-cacher-accent: mat-palette($mat-pink, A200, A100, A400); +// // Define the palettes for your theme using the Material Design palettes available in palette.scss +// // (imported above). For each palette, you can optionally specify a default, lighter, and darker +// // hue. Available color palettes: https://material.io/design/color/ +// $public-gateway-cacher-primary: mat-palette($mat-indigo); +// $public-gateway-cacher-accent: mat-palette($mat-pink, A200, A100, A400); -// The warn palette is optional (defaults to red). -$public-gateway-cacher-warn: mat-palette($mat-red); +// // The warn palette is optional (defaults to red). +// $public-gateway-cacher-warn: mat-palette($mat-red); -// Create the theme object (a Sass map containing all of the palettes). -$public-gateway-cacher-theme: mat-light-theme($public-gateway-cacher-primary, $public-gateway-cacher-accent, $public-gateway-cacher-warn); +// // Create the theme object (a Sass map containing all of the palettes). +// $public-gateway-cacher-theme: mat-light-theme($public-gateway-cacher-primary, $public-gateway-cacher-accent, $public-gateway-cacher-warn); -// Include theme styles for core and each component used in your app. -// Alternatively, you can import and @include the theme mixins for each component -// that you are using. -@include angular-material-theme($public-gateway-cacher-theme); +// // Include theme styles for core and each component used in your app. +// // Alternatively, you can import and @include the theme mixins for each component +// // that you are using. +// @include angular-material-theme($public-gateway-cacher-theme); + +@import 'styles/ipfs-themes.scss'; + +.theme-light { + @include angular-material-theme($ipfs-light-theme); +} + +.theme-dark { + @include angular-material-theme($ipfs-dark-theme); +} /* You can add global styles to this file, and also import other style files */ diff --git a/src/styles/ipfs-colours.scss b/src/styles/ipfs-colours.scss new file mode 100644 index 0000000..45ee2d9 --- /dev/null +++ b/src/styles/ipfs-colours.scss @@ -0,0 +1,23 @@ +$ipfs-colour-navy: (default: #0B3A53, + lighter: #3e6480, + darker: #00142a, + contrast: (default: $light-primary-text, + lighter: $light-primary-text, + darker: $light-primary-text, + )); + +$ipfs-colour-aqua: (default: #69c5cd, + lighter: #9df8ff, + darker: #32949c, + contrast: (default: $dark-primary-text, + lighter: $dark-primary-text, + darker: $dark-primary-text, + )); + +$ipfs-colour-yellow: (default: #f39021, + lighter: #ffc155, + darker: #bb6200, + contrast: (default: $dark-primary-text, + lighter: $dark-primary-text, + darker: $dark-primary-text, + )); diff --git a/src/styles/ipfs-palettes.scss b/src/styles/ipfs-palettes.scss new file mode 100644 index 0000000..5f9f988 --- /dev/null +++ b/src/styles/ipfs-palettes.scss @@ -0,0 +1,5 @@ +@import 'ipfs-colours.scss'; + +$ipfs-primary: mat-palette($ipfs-colour-navy, 'default', 'lighter', 'darker'); +$ipfs-accent: mat-palette($ipfs-colour-aqua, 'default', 'lighter', 'darker'); +$ipfs-warn: mat-palette($ipfs-colour-yellow, 'default', 'lighter', 'darker'); diff --git a/src/styles/ipfs-themes.scss b/src/styles/ipfs-themes.scss new file mode 100644 index 0000000..133cc06 --- /dev/null +++ b/src/styles/ipfs-themes.scss @@ -0,0 +1,4 @@ +@import 'ipfs-palettes.scss'; + +$ipfs-light-theme: mat-light-theme($ipfs-primary, $ipfs-accent, $ipfs-warn); +$ipfs-dark-theme: mat-dark-theme($ipfs-primary, $ipfs-accent, $ipfs-warn);