From 9ff6d76cf293e842117e444996ed760a4f58fbea Mon Sep 17 00:00:00 2001 From: grimsi <9295182+grimsi@users.noreply.github.com> Date: Mon, 15 Aug 2022 14:31:44 +0200 Subject: [PATCH] Refactored and improved dark-mode implementation --- frontend/src/app/app.module.ts | 2 +- .../components/footer/footer.component.scss | 4 +- .../components/header/header.component.html | 3 +- .../app/components/header/header.component.ts | 69 +++--------------- .../library-management.component.scss | 9 --- .../library-management.component.ts | 4 +- .../library-overview.component.html | 29 +++++--- .../library-overview.component.scss | 16 ++-- .../library-overview.component.ts | 11 ++- .../src/app/services/cookie.service.spec.ts | 16 ++++ frontend/src/app/services/cookie.service.ts | 34 +++++++++ .../src/app/services/theming.service.spec.ts | 16 ++++ frontend/src/app/services/theming.service.ts | 53 ++++++++++++++ .../dark-theme.scss} | 2 +- frontend/src/app/themes/light-theme.scss | 14 ++++ .../src/assets/Gameyfin_Logo_256px_dark.png | Bin 0 -> 3006 bytes frontend/src/styles.scss | 16 ++-- 17 files changed, 192 insertions(+), 106 deletions(-) create mode 100644 frontend/src/app/services/cookie.service.spec.ts create mode 100644 frontend/src/app/services/cookie.service.ts create mode 100644 frontend/src/app/services/theming.service.spec.ts create mode 100644 frontend/src/app/services/theming.service.ts rename frontend/src/app/{theme/default-theme.scss => themes/dark-theme.scss} (90%) create mode 100644 frontend/src/app/themes/light-theme.scss create mode 100644 frontend/src/assets/Gameyfin_Logo_256px_dark.png diff --git a/frontend/src/app/app.module.ts b/frontend/src/app/app.module.ts index 35513e0..cf3c62d 100644 --- a/frontend/src/app/app.module.ts +++ b/frontend/src/app/app.module.ts @@ -121,7 +121,7 @@ import {MatExpansionModule} from "@angular/material/expansion"; }, { provide: MAT_SNACK_BAR_DEFAULT_OPTIONS, - useValue: { panelClass: ['snackbar-dark'] }, + useValue: { panelClass: ['formatted-snackbar'] }, } ], bootstrap: [AppComponent] diff --git a/frontend/src/app/components/footer/footer.component.scss b/frontend/src/app/components/footer/footer.component.scss index b7d02fb..47698af 100644 --- a/frontend/src/app/components/footer/footer.component.scss +++ b/frontend/src/app/components/footer/footer.component.scss @@ -1,9 +1,9 @@ @use 'sass:map'; @use '@angular/material' as mat; -@import '../../theme/default-theme'; +@import 'src/app/themes/light-theme'; a { - $config: mat.get-color-config($custom-theme); + $config: mat.get-color-config($light-theme); $primary-palette: map.get($config, 'primary'); color: mat.get-color-from-palette($primary-palette, 500); } diff --git a/frontend/src/app/components/header/header.component.html b/frontend/src/app/components/header/header.component.html index dd877db..ab0068b 100644 --- a/frontend/src/app/components/header/header.component.html +++ b/frontend/src/app/components/header/header.component.html @@ -5,7 +5,8 @@ - + + + + + + + {{game.title}} - + @@ -46,11 +55,11 @@
- Offline Co-op + Offline Co-op - Online Co-op + Online Co-op - LAN Support + LAN Support
@@ -80,7 +89,7 @@ -
+
diff --git a/frontend/src/app/components/library-overview/library-overview.component.scss b/frontend/src/app/components/library-overview/library-overview.component.scss index d5703ae..28825b2 100644 --- a/frontend/src/app/components/library-overview/library-overview.component.scss +++ b/frontend/src/app/components/library-overview/library-overview.component.scss @@ -1,6 +1,6 @@ @use 'sass:map'; @use '@angular/material' as mat; -@import '../../theme/default-theme'; +@import 'src/app/themes/dark-theme'; .fullscreen-overlay { position: absolute; @@ -19,21 +19,15 @@ @include mat.elevation(16); - $config: mat.get-color-config($custom-theme); - $background: map.get($config, background); position: absolute; right: 56px; top: 72px; width: 250px; border-radius: 6px; - background: mat.get-color-from-palette($background, app-bar); - border-color: mat.get-color-from-palette($background, app-bar); - border-style: solid; - color: white; p { padding: 0 12px 12px 16px; - } + } } .content { @@ -45,13 +39,13 @@ } ::ng-deep .mat-checkbox-frame { - $config: mat.get-color-config($custom-theme); + $config: mat.get-color-config($dark-theme); $primary-palette: map.get($config, 'primary'); - border-color: mat.get-color-from-palette($primary-palette, 500); + border-color: mat.get-color-from-palette($primary-palette, 500) !important; } ::ng-deep .mat-form-field-underline { - $config: mat.get-color-config($custom-theme); + $config: mat.get-color-config($dark-theme); $primary-palette: map.get($config, 'primary'); background-color: mat.get-color-from-palette($primary-palette, 500) !important; } diff --git a/frontend/src/app/components/library-overview/library-overview.component.ts b/frontend/src/app/components/library-overview/library-overview.component.ts index 774ad40..e5b176f 100644 --- a/frontend/src/app/components/library-overview/library-overview.component.ts +++ b/frontend/src/app/components/library-overview/library-overview.component.ts @@ -46,13 +46,22 @@ export class LibraryOverviewComponent implements AfterContentInit { forkJoin([themeObservable, genreObservable]).subscribe(result => { this.availableThemes = result[0]; this.availableGenres = result[1]; - this.filterGames(); + this.refreshLibraryView(); this.loading = false; }); } ); } + refreshLibraryView(): void { + this.filterGames(); + } + + clearSearchTerm(): void { + this.searchTerm = ""; + this.refreshLibraryView(); + } + filterGames(): void { this.gameServerService.getAllGames().subscribe(games => { let filteredGames: DetectedGameDto[] = games; diff --git a/frontend/src/app/services/cookie.service.spec.ts b/frontend/src/app/services/cookie.service.spec.ts new file mode 100644 index 0000000..43ea274 --- /dev/null +++ b/frontend/src/app/services/cookie.service.spec.ts @@ -0,0 +1,16 @@ +import { TestBed } from '@angular/core/testing'; + +import { CookieService } from './cookie.service'; + +describe('CookieService', () => { + let service: CookieService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(CookieService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/frontend/src/app/services/cookie.service.ts b/frontend/src/app/services/cookie.service.ts new file mode 100644 index 0000000..00af0e3 --- /dev/null +++ b/frontend/src/app/services/cookie.service.ts @@ -0,0 +1,34 @@ +import {Injectable} from '@angular/core'; + +@Injectable({ + providedIn: 'root' +}) +export class CookieService { + + constructor() { + } + + setCookie(name: string, value: any): void { + document.cookie = `${name}=${value.toString()};`; + } + + getCookie(name: string): string | null { + let end; + const dc = document.cookie; + const prefix = name + "="; + let begin = dc.indexOf("; " + prefix); + + if (begin == -1) { + begin = dc.indexOf(prefix); + if (begin != 0) return null; + } else { + begin += 2; + end = document.cookie.indexOf(";", begin); + if (end == -1) { + end = dc.length; + } + } + + return decodeURI(dc.substring(begin + prefix.length, end)); + } +} diff --git a/frontend/src/app/services/theming.service.spec.ts b/frontend/src/app/services/theming.service.spec.ts new file mode 100644 index 0000000..f932a5e --- /dev/null +++ b/frontend/src/app/services/theming.service.spec.ts @@ -0,0 +1,16 @@ +import { TestBed } from '@angular/core/testing'; + +import { ThemingService } from './theming.service'; + +describe('ThemingService', () => { + let service: ThemingService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(ThemingService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/frontend/src/app/services/theming.service.ts b/frontend/src/app/services/theming.service.ts new file mode 100644 index 0000000..787718b --- /dev/null +++ b/frontend/src/app/services/theming.service.ts @@ -0,0 +1,53 @@ +import {Injectable} from '@angular/core'; +import {OverlayContainer} from "@angular/cdk/overlay"; +import {CookieService} from "./cookie.service"; + +@Injectable({ + providedIn: 'root' +}) +export class ThemingService { + + private darkmodeEnabled!: boolean; + private darkmodeClassName: string = 'darkMode'; + + constructor(private cookieService: CookieService, + private overlay: OverlayContainer) { + if (this.cookieService.getCookie("darkmode") !== null) { + this.darkmodeEnabled = this.cookieService.getCookie("darkmode") === "true"; + } else if (window.matchMedia) { + this.darkmodeEnabled = window.matchMedia('(prefers-color-scheme: dark)').matches; + } else { + this.darkmodeEnabled = false; + } + + this.setTheme(); + } + + toggleTheme(): void { + this.darkmodeEnabled = !this.darkmodeEnabled; + this.setTheme(); + } + + private setTheme(): void { + this.darkmodeEnabled ? this.setDarkmode() : this.setLightmode(); + this.cookieService.setCookie("darkmode", this.darkmodeEnabled); + } + + private setDarkmode(): void { + document.body.classList.add(this.darkmodeClassName); + document.body.style.colorScheme = "dark"; + document.body.style.background = "#303030"; + document.body.style.color = "white"; + + this.overlay.getContainerElement().classList.add(this.darkmodeClassName); + } + + private setLightmode(): void { + document.body.classList.remove(this.darkmodeClassName); + document.body.style.colorScheme = "light"; + document.body.style.background = "white"; + document.body.style.color = "black"; + + this.overlay.getContainerElement().classList.remove(this.darkmodeClassName); + } +} diff --git a/frontend/src/app/theme/default-theme.scss b/frontend/src/app/themes/dark-theme.scss similarity index 90% rename from frontend/src/app/theme/default-theme.scss rename to frontend/src/app/themes/dark-theme.scss index 93993e4..e0a9323 100644 --- a/frontend/src/app/theme/default-theme.scss +++ b/frontend/src/app/themes/dark-theme.scss @@ -5,7 +5,7 @@ $custom-theme-primary: mat.define-palette(mat.$green-palette); $custom-theme-accent: mat.define-palette(mat.$grey-palette, A200, A100, A400); $custom-theme-warn: mat.define-palette(mat.$red-palette); -$custom-theme: mat.define-dark-theme(( +$dark-theme: mat.define-dark-theme(( color: ( primary: $custom-theme-primary, accent: $custom-theme-accent, diff --git a/frontend/src/app/themes/light-theme.scss b/frontend/src/app/themes/light-theme.scss new file mode 100644 index 0000000..83897e1 --- /dev/null +++ b/frontend/src/app/themes/light-theme.scss @@ -0,0 +1,14 @@ +@use '@angular/material' as mat; +@import "@angular/material/theming"; + +$custom-theme-primary: mat.define-palette(mat.$green-palette); +$custom-theme-accent: mat.define-palette(mat.$grey-palette, A200, A100, A400); +$custom-theme-warn: mat.define-palette(mat.$red-palette); + +$light-theme: mat.define-light-theme(( + color: ( + primary: $custom-theme-primary, + accent: $custom-theme-accent, + warn: $custom-theme-warn + ) +)); diff --git a/frontend/src/assets/Gameyfin_Logo_256px_dark.png b/frontend/src/assets/Gameyfin_Logo_256px_dark.png new file mode 100644 index 0000000000000000000000000000000000000000..b6be344adb9bb372cf785587cb104d13ac9c76bc GIT binary patch literal 3006 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