Add IPFS theme
parent
f55110d6df
commit
dbeb7b3e22
|
@ -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
|
||||
|
||||
|
|
|
@ -1 +1,10 @@
|
|||
<mat-toolbar color="primary" fxLayout="row" fxLayoutAlign="space-between center" fxLayoutGap="1em">
|
||||
<h1>Public IPFS Cacher</h1>
|
||||
|
||||
<!-- Theme Switcher -->
|
||||
<button mat-icon-button="">
|
||||
<mat-icon (click)="themeService.switchTheme()">{{themeService.icon}}</mat-icon>
|
||||
</button>
|
||||
|
||||
</mat-toolbar>
|
||||
<router-outlet></router-outlet>
|
||||
|
|
|
@ -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
|
||||
) { }
|
||||
}
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
export enum Theme {
|
||||
Light = 'theme-light',
|
||||
Dark = 'theme-dark',
|
||||
}
|
|
@ -1,12 +1,10 @@
|
|||
<div class="container" fxLayout="column" fxLayoutAlign="space-evenly center" fxLayoutGap="1em">
|
||||
|
||||
<h1>Public IPFS Cacher</h1>
|
||||
|
||||
<!-- IPFS -->
|
||||
<form fxLayout="row" fxLayoutAlign="space-evenly center" fxLayoutGap="1em">
|
||||
|
||||
<!-- Hash -->
|
||||
<mat-form-field>
|
||||
<mat-form-field [color]="inputColour">
|
||||
<mat-label>IPFS</mat-label>
|
||||
<input name="ipfs" [(ngModel)]="ipfs" matInput>
|
||||
</mat-form-field>
|
||||
|
@ -20,7 +18,7 @@
|
|||
<form fxLayout="row" fxLayoutAlign="space-evenly center" fxLayoutGap="1em">
|
||||
|
||||
<!-- Hash -->
|
||||
<mat-form-field>
|
||||
<mat-form-field [color]="inputColour">
|
||||
<mat-label>IPNS</mat-label>
|
||||
<input name="ipns" [(ngModel)]="ipns" matInput>
|
||||
</mat-form-field>
|
||||
|
@ -30,7 +28,8 @@
|
|||
|
||||
</form>
|
||||
|
||||
<mat-progress-bar [value]="dataSource.data.length / gateways.length * 100" *ngIf="subscriptions.length" mode="determinate">
|
||||
<mat-progress-bar [value]="dataSource.data.length / gateways.length * 100" *ngIf="subscriptions.length" mode="determinate"
|
||||
color="primary">
|
||||
</mat-progress-bar>
|
||||
|
||||
<table class="mat-elevation-z8" [dataSource]="dataSource" mat-table>
|
||||
|
@ -47,15 +46,15 @@
|
|||
<ng-container matColumnDef="gateway">
|
||||
<th mat-header-cell *matHeaderCellDef> Gateway </th>
|
||||
<td mat-cell *matCellDef="let element">
|
||||
<div [ngSwitch]="element.error">
|
||||
<div *ngSwitchCase="null">
|
||||
<div [ngSwitch]="element.ok">
|
||||
<div *ngSwitchCase="true">
|
||||
<strong>
|
||||
<a class="aqua" href="{{ element.gateway }}#x-ipfs-companion-no-redirect" target="_blank">
|
||||
{{ element.gateway }}
|
||||
</a>
|
||||
</strong>
|
||||
</div>
|
||||
<div *ngSwitchDefault>
|
||||
<div *ngSwitchCase="false">
|
||||
<a class="aqua-muted" href="{{ element.gateway }}#x-ipfs-companion-no-redirect" target="_blank">
|
||||
{{ element.gateway }}
|
||||
</a>
|
||||
|
|
|
@ -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<Result>;
|
||||
|
||||
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<void>();
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -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<HttpResponseBase> {
|
||||
return this.http.get<HttpResponseBase>(`${this.url(gateway, protocol, hashpath)}#x-ipfs-companion-no-redirect`, {
|
||||
observe: 'response'
|
||||
get(gateway: string, protocol: Protocol, hashpath: string): Observable<HttpResponse<string>> {
|
||||
return this.http.get(`${this.url(gateway, protocol, hashpath)}#x-ipfs-companion-no-redirect`, {
|
||||
observe: 'response',
|
||||
responseType: 'text',
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,49 @@
|
|||
import { Injectable } from '@angular/core';
|
||||
import { Observable, Subject } from 'rxjs';
|
||||
import { Theme } from '../enums/theme.enum';
|
||||
|
||||
function enumGuard<T>(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<Theme>;
|
||||
|
||||
private readonly subject$ = new Subject<Theme>();
|
||||
|
||||
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';
|
||||
}
|
||||
}
|
||||
}
|
|
@ -19,7 +19,7 @@
|
|||
|
||||
</head>
|
||||
|
||||
<body class="mat-app-background">
|
||||
<body class="mat-app-background theme-light">
|
||||
<app-root></app-root>
|
||||
</body>
|
||||
|
||||
|
|
|
@ -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 */
|
||||
|
||||
|
|
|
@ -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,
|
||||
));
|
|
@ -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');
|
|
@ -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);
|
Loading…
Reference in New Issue