mirror of
https://github.com/xuthus83/pigallery2.git
synced 2024-11-03 21:04:03 +08:00
parent
2287893bf4
commit
609d2127d9
@ -489,6 +489,20 @@ export class ClientLightboxConfig {
|
||||
loopVideos: boolean = false;
|
||||
}
|
||||
|
||||
@SubConfigClass<TAGS>({tags: {client: true}, softReadonly: true})
|
||||
export class ThemesConfig {
|
||||
|
||||
@ConfigProperty({
|
||||
tags: {
|
||||
name: $localize`Enable`,
|
||||
experimental: true
|
||||
} as TAGS,
|
||||
description: $localize`Enable themes and color modes.`
|
||||
})
|
||||
enabled: boolean = false;
|
||||
}
|
||||
|
||||
|
||||
@SubConfigClass<TAGS>({tags: {client: true}, softReadonly: true})
|
||||
export class ClientGalleryConfig {
|
||||
@ConfigProperty({
|
||||
@ -578,6 +592,7 @@ export class ClientGalleryConfig {
|
||||
description: $localize`Adds a button to flattens the file structure, by listing the content of all subdirectories. (Won't work if the gallery has multiple folders with the same path.)`
|
||||
})
|
||||
enableDirectoryFlattening: boolean = false;
|
||||
|
||||
@ConfigProperty({
|
||||
tags: {
|
||||
name: $localize`Lightbox`,
|
||||
@ -585,6 +600,14 @@ export class ClientGalleryConfig {
|
||||
},
|
||||
})
|
||||
Lightbox: ClientLightboxConfig = new ClientLightboxConfig();
|
||||
|
||||
@ConfigProperty({
|
||||
tags: {
|
||||
name: $localize`ThemesConfig`,
|
||||
priority: ConfigPriority.advanced,
|
||||
},
|
||||
})
|
||||
Themes: ThemesConfig = new ThemesConfig();
|
||||
}
|
||||
|
||||
@SubConfigClass({tags: {client: true}, softReadonly: true})
|
||||
|
@ -104,6 +104,7 @@ import {JobProgressComponent} from './ui/settings/workflow/progress/job-progress
|
||||
import {SettingsEntryComponent} from './ui/settings/template/settings-entry/settings-entry.component';
|
||||
import {UsersComponent} from './ui/settings/users/users.component';
|
||||
import {SharingsListComponent} from './ui/settings/sharings-list/sharings-list.component';
|
||||
import {ThemeService} from './model/theme.service';
|
||||
|
||||
@Injectable()
|
||||
export class MyHammerConfig extends HammerGestureConfig {
|
||||
@ -267,6 +268,7 @@ Marker.prototype.options.icon = iconDefault;
|
||||
SeededRandomService,
|
||||
OverlayService,
|
||||
QueryService,
|
||||
ThemeService,
|
||||
DuplicateService,
|
||||
FacesService,
|
||||
VersionService,
|
||||
|
82
src/frontend/app/model/theme.service.ts
Normal file
82
src/frontend/app/model/theme.service.ts
Normal file
@ -0,0 +1,82 @@
|
||||
import {Injectable} from '@angular/core';
|
||||
import {BehaviorSubject} from 'rxjs';
|
||||
import {Config} from '../../../common/config/public/Config';
|
||||
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class ThemeService {
|
||||
mode: ThemeMode = ThemeMode.light;
|
||||
public readonly darkMode: BehaviorSubject<boolean> = new BehaviorSubject(false);
|
||||
public readonly matcher = window.matchMedia('(prefers-color-scheme: dark)');
|
||||
|
||||
constructor() {
|
||||
this.darkMode.subscribe((darkMode: boolean) => {
|
||||
this.applyMode(darkMode);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
listenToModePreference() {
|
||||
if (this.mode !== ThemeMode.auto) {
|
||||
return;
|
||||
}
|
||||
this.darkMode.next(window.matchMedia('(prefers-color-scheme: dark)').matches);
|
||||
|
||||
this.matcher.addEventListener('change', event => {
|
||||
this.darkMode.next(event.matches);
|
||||
});
|
||||
}
|
||||
|
||||
stopListening() {
|
||||
this.matcher.removeAllListeners();
|
||||
}
|
||||
|
||||
applyMode(darkMode: boolean) {
|
||||
if (!Config.Gallery.Themes.enabled) {
|
||||
return;
|
||||
}
|
||||
if (!darkMode) {
|
||||
document.documentElement.removeAttribute('data-bs-theme');
|
||||
} else {
|
||||
document.documentElement.setAttribute('data-bs-theme', 'dark');
|
||||
}
|
||||
}
|
||||
|
||||
setMode(mode: ThemeMode) {
|
||||
this.mode = mode;
|
||||
if (this.mode === ThemeMode.light) {
|
||||
this.darkMode.next(false);
|
||||
this.stopListening();
|
||||
} else if (this.mode === ThemeMode.dark) {
|
||||
this.darkMode.next(true);
|
||||
this.stopListening();
|
||||
} else if (this.mode === ThemeMode.auto) {
|
||||
this.listenToModePreference();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
toggleMode() {
|
||||
switch (this.mode) {
|
||||
case ThemeMode.light:
|
||||
this.setMode(ThemeMode.dark);
|
||||
break;
|
||||
case ThemeMode.dark:
|
||||
this.setMode(ThemeMode.auto);
|
||||
break;
|
||||
case ThemeMode.auto:
|
||||
this.setMode(ThemeMode.light);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export enum ThemeMode {
|
||||
light = 1, dark, auto
|
||||
}
|
||||
|
||||
export enum AppliedThemeMode {
|
||||
light = 1, dark
|
||||
}
|
@ -63,7 +63,7 @@
|
||||
<li class="nav-item divider-vertical d-xl-block d-none">
|
||||
</li>
|
||||
<li class="nav-item dropdown">
|
||||
<div class="btn-group" dropdown #dropdown="bs-dropdown" placement="bottom"
|
||||
<div class="btn-group" dropdown #dropdown="bs-dropdown" placement="bottom"
|
||||
[insideClick]="true">
|
||||
<button id="button-alignment" dropdownToggle
|
||||
type="button" class="btn btn-dark dropdown-toggle"
|
||||
@ -87,7 +87,28 @@
|
||||
<li class="nav-item d-xl-none" *ngIf="showShare">
|
||||
<app-gallery-share [dropDownItem]="true"></app-gallery-share>
|
||||
</li>
|
||||
<hr class="d-xl-none"/>
|
||||
<li role="menuitem" *ngIf="themesEnabled">
|
||||
<div class="dropdown-item bg-transparent">
|
||||
<button class="btn w-100 btn-secondary" (click)="themeService.toggleMode()">
|
||||
<ng-container [ngSwitch]="themeService.mode">
|
||||
<ng-container *ngSwitchCase="ThemeMode.light">
|
||||
<span class="oi oi-sun"></span>
|
||||
<ng-container i18n>Light</ng-container>
|
||||
</ng-container>
|
||||
<ng-container *ngSwitchCase="ThemeMode.dark">
|
||||
<span class="oi oi-moon"></span>
|
||||
<ng-container i18n>Dark</ng-container>
|
||||
</ng-container>
|
||||
<ng-container *ngSwitchCase="ThemeMode.auto">
|
||||
<span class="oi oi-moon"></span>
|
||||
<span class="oi oi-sun" style="margin-left: -0.8rem"></span>
|
||||
<ng-container i18n>Auto</ng-container>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
</button>
|
||||
</div>
|
||||
</li>
|
||||
<hr/>
|
||||
<ng-container *ngFor="let link of navbarLinks">
|
||||
<li class="nav-item d-md-none"
|
||||
*ngIf="link.type === NavigationLinkTypes.albums && isAlbumsAvailable()">
|
||||
|
@ -12,6 +12,7 @@ import {Utils} from '../../../../common/Utils';
|
||||
import {PageHelper} from '../../model/page.helper';
|
||||
import {BsDropdownDirective} from 'ngx-bootstrap/dropdown';
|
||||
import {LanguageComponent} from '../language/language.component';
|
||||
import {ThemeMode, ThemeService} from '../../model/theme.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-frame',
|
||||
@ -30,6 +31,7 @@ export class FrameComponent {
|
||||
public readonly navbarLinks = Config.Gallery.NavBar.links;
|
||||
public readonly NavigationLinkTypes = NavigationLinkTypes;
|
||||
public readonly stringify = JSON.stringify;
|
||||
public readonly themesEnabled = Config.Gallery.Themes.enabled;
|
||||
|
||||
/* sticky top navbar */
|
||||
private lastScroll = {
|
||||
@ -44,11 +46,14 @@ export class FrameComponent {
|
||||
@ViewChild('dropdown', {static: true}) dropdown: BsDropdownDirective;
|
||||
@ViewChild('languageSelector', {static: true}) languageSelector: LanguageComponent;
|
||||
|
||||
ThemeMode = ThemeMode;
|
||||
|
||||
constructor(
|
||||
private authService: AuthenticationService,
|
||||
public notificationService: NotificationService,
|
||||
public queryService: QueryService,
|
||||
private router: Router
|
||||
private router: Router,
|
||||
public themeService: ThemeService
|
||||
) {
|
||||
this.user = this.authService.user;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user