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;
|
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})
|
@SubConfigClass<TAGS>({tags: {client: true}, softReadonly: true})
|
||||||
export class ClientGalleryConfig {
|
export class ClientGalleryConfig {
|
||||||
@ConfigProperty({
|
@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.)`
|
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;
|
enableDirectoryFlattening: boolean = false;
|
||||||
|
|
||||||
@ConfigProperty({
|
@ConfigProperty({
|
||||||
tags: {
|
tags: {
|
||||||
name: $localize`Lightbox`,
|
name: $localize`Lightbox`,
|
||||||
@ -585,6 +600,14 @@ export class ClientGalleryConfig {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
Lightbox: ClientLightboxConfig = new ClientLightboxConfig();
|
Lightbox: ClientLightboxConfig = new ClientLightboxConfig();
|
||||||
|
|
||||||
|
@ConfigProperty({
|
||||||
|
tags: {
|
||||||
|
name: $localize`ThemesConfig`,
|
||||||
|
priority: ConfigPriority.advanced,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
Themes: ThemesConfig = new ThemesConfig();
|
||||||
}
|
}
|
||||||
|
|
||||||
@SubConfigClass({tags: {client: true}, softReadonly: true})
|
@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 {SettingsEntryComponent} from './ui/settings/template/settings-entry/settings-entry.component';
|
||||||
import {UsersComponent} from './ui/settings/users/users.component';
|
import {UsersComponent} from './ui/settings/users/users.component';
|
||||||
import {SharingsListComponent} from './ui/settings/sharings-list/sharings-list.component';
|
import {SharingsListComponent} from './ui/settings/sharings-list/sharings-list.component';
|
||||||
|
import {ThemeService} from './model/theme.service';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class MyHammerConfig extends HammerGestureConfig {
|
export class MyHammerConfig extends HammerGestureConfig {
|
||||||
@ -267,6 +268,7 @@ Marker.prototype.options.icon = iconDefault;
|
|||||||
SeededRandomService,
|
SeededRandomService,
|
||||||
OverlayService,
|
OverlayService,
|
||||||
QueryService,
|
QueryService,
|
||||||
|
ThemeService,
|
||||||
DuplicateService,
|
DuplicateService,
|
||||||
FacesService,
|
FacesService,
|
||||||
VersionService,
|
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
|
||||||
|
}
|
@ -87,7 +87,28 @@
|
|||||||
<li class="nav-item d-xl-none" *ngIf="showShare">
|
<li class="nav-item d-xl-none" *ngIf="showShare">
|
||||||
<app-gallery-share [dropDownItem]="true"></app-gallery-share>
|
<app-gallery-share [dropDownItem]="true"></app-gallery-share>
|
||||||
</li>
|
</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">
|
<ng-container *ngFor="let link of navbarLinks">
|
||||||
<li class="nav-item d-md-none"
|
<li class="nav-item d-md-none"
|
||||||
*ngIf="link.type === NavigationLinkTypes.albums && isAlbumsAvailable()">
|
*ngIf="link.type === NavigationLinkTypes.albums && isAlbumsAvailable()">
|
||||||
|
@ -12,6 +12,7 @@ import {Utils} from '../../../../common/Utils';
|
|||||||
import {PageHelper} from '../../model/page.helper';
|
import {PageHelper} from '../../model/page.helper';
|
||||||
import {BsDropdownDirective} from 'ngx-bootstrap/dropdown';
|
import {BsDropdownDirective} from 'ngx-bootstrap/dropdown';
|
||||||
import {LanguageComponent} from '../language/language.component';
|
import {LanguageComponent} from '../language/language.component';
|
||||||
|
import {ThemeMode, ThemeService} from '../../model/theme.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-frame',
|
selector: 'app-frame',
|
||||||
@ -30,6 +31,7 @@ export class FrameComponent {
|
|||||||
public readonly navbarLinks = Config.Gallery.NavBar.links;
|
public readonly navbarLinks = Config.Gallery.NavBar.links;
|
||||||
public readonly NavigationLinkTypes = NavigationLinkTypes;
|
public readonly NavigationLinkTypes = NavigationLinkTypes;
|
||||||
public readonly stringify = JSON.stringify;
|
public readonly stringify = JSON.stringify;
|
||||||
|
public readonly themesEnabled = Config.Gallery.Themes.enabled;
|
||||||
|
|
||||||
/* sticky top navbar */
|
/* sticky top navbar */
|
||||||
private lastScroll = {
|
private lastScroll = {
|
||||||
@ -44,11 +46,14 @@ export class FrameComponent {
|
|||||||
@ViewChild('dropdown', {static: true}) dropdown: BsDropdownDirective;
|
@ViewChild('dropdown', {static: true}) dropdown: BsDropdownDirective;
|
||||||
@ViewChild('languageSelector', {static: true}) languageSelector: LanguageComponent;
|
@ViewChild('languageSelector', {static: true}) languageSelector: LanguageComponent;
|
||||||
|
|
||||||
|
ThemeMode = ThemeMode;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private authService: AuthenticationService,
|
private authService: AuthenticationService,
|
||||||
public notificationService: NotificationService,
|
public notificationService: NotificationService,
|
||||||
public queryService: QueryService,
|
public queryService: QueryService,
|
||||||
private router: Router
|
private router: Router,
|
||||||
|
public themeService: ThemeService
|
||||||
) {
|
) {
|
||||||
this.user = this.authService.user;
|
this.user = this.authService.user;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user