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 0000000..b6be344 Binary files /dev/null and b/frontend/src/assets/Gameyfin_Logo_256px_dark.png differ diff --git a/frontend/src/styles.scss b/frontend/src/styles.scss index 1b418e3..5ea48e4 100644 --- a/frontend/src/styles.scss +++ b/frontend/src/styles.scss @@ -4,7 +4,8 @@ @use '@angular/material' as mat; // Plus imports for other components in your app. -@import "src/app/theme/default-theme"; +@import "src/app/themes/light-theme"; +@import "src/app/themes/dark-theme"; // Include the common styles for Angular Material. We include this here so that you only // have to load a single css file for Angular Material in your app. // Be sure that you only ever include this mixin once! @@ -13,7 +14,11 @@ // 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 mat.all-component-themes($custom-theme); +@include mat.all-component-themes($light-theme); + +.darkMode { + @include mat.all-component-colors($dark-theme); +} /* You can add global styles to this file, and also import other style files */ html, body { height: 100%; } @@ -23,12 +28,7 @@ html { overflow-y: scroll; } -.snackbar-dark { - color: white; - $config: mat.get-color-config($custom-theme); - $background: map.get($config, background); - background: mat.get-color-from-palette($background, app-bar); - +.formatted-snackbar { // add support for formatting (newlines) white-space: pre-wrap; }