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;
}